My problem is:
Livolo switches have their own Zigbee gate. I want to connect them from zigbee2mqtt with CC2531 USB dongle. In general it works, but when I turn on/off switch button (on physical device), the switch sends an incorrect ZCL package.
I'm an absolutely newbie in microcontrollers programming and in Zigbee architecture. So I hope someone can help me and answer these questions:
Where I can intercept that malformed package?
How I can fix that package to support the Zigbee standard?
I use Z-STACK-HOME 1.2.2a firmware and compile it as described there:
https://github.com/Koenkk/Z-Stack-firmware/blob/master/coordinator/Z-Stack_Home_1.2/COMPILE.md
// Malformed ZCL package
// header
0x7c, // [0111 1100]
// 01 - frame type = "Command is specific or local to a cluster"
// 1 - manufacturer spec = manufacturer code present
// 1 - direction = "from server to client"
// 1 - disable default response
// 100 - reserved
0xd2, 0x15, // manufacturer
0xd8, // sequence
0x00, // read attrs command
// endpoint adress
0x12, 0x0f, 0x05, 0x18, 0x00, 0x4b, 0x12, 0x00,
0x22, 0x00, // ????? need more data from another switches
0x03 // 0x00|0x01|0x02|0x03 - switch state
upd:
I think, what I can intercept message in AF.c file in afIncomingData function and fix in afBuildMSGIncoming.
So now I hope, someone can help me with the right message format. Which can be processed standard ZCL parcer.
void afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress, uint16 SrcPanId,
NLDE_Signal_t *sig, uint8 nwkSeqNum, uint8 SecurityUse,
uint32 timestamp, uint8 radius )
{
endPointDesc_t *epDesc = NULL;
epList_t *pList = epList;
#if !defined ( APS_NO_GROUPS )
uint8 grpEp = APS_GROUPS_EP_NOT_FOUND;
#endif
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
#if !defined ( APS_NO_GROUPS )
// Find the first endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, APS_GROUPS_FIND_FIRST );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND ) {
// No endpoint found, default to endpoint 1.
// In the original source code there is a return here.
// This prevent the messags from being forwarded.
// For our use-case we want to capture all messages.
// Even if the coordinator is not in the group.
epDesc = afFindEndPointDesc( 1 );
}
else {
epDesc = afFindEndPointDesc( grpEp );
}
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint );
#else
return; // Not supported
#endif
}
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
// Set the list
if ( pList != NULL )
{
epDesc = pList->epDesc;
}
}
else if ( aff->DstEndPoint == 10 || aff->DstEndPoint == 11 ) {
if ( (epDesc = afFindEndPointDesc( 1 )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
}
else if ( (epDesc = afFindEndPointDesc( aff->DstEndPoint )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
while ( epDesc )
{
uint16 epProfileID = 0xFFFE; // Invalid Profile ID
if ( pList->pfnDescCB )
{
uint16 *pID = (uint16 *)(pList->pfnDescCB(
AF_DESCRIPTOR_PROFILE_ID, epDesc->endPoint ));
if ( pID )
{
epProfileID = *pID;
osal_mem_free( pID );
}
}
else if ( epDesc->simpleDesc )
{
epProfileID = epDesc->simpleDesc->AppProfId;
}
// First part of verification is to make sure that:
// the local Endpoint ProfileID matches the received ProfileID OR
// the message is specifically send to ZDO (this excludes the broadcast endpoint) OR
// if the Wildcard ProfileID is received the message should not be sent to ZDO endpoint
if ( (aff->ProfileID == epProfileID) ||
((epDesc->endPoint == ZDO_EP) && (aff->ProfileID == ZDO_PROFILE_ID)) ||
((epDesc->endPoint != ZDO_EP) && ( aff->ProfileID == ZDO_WILDCARD_PROFILE_ID )) )
{
// Save original endpoint
uint8 endpoint = aff->DstEndPoint;
// overwrite with descriptor's endpoint
aff->DstEndPoint = epDesc->endPoint;
afBuildMSGIncoming( aff, epDesc, SrcAddress, SrcPanId, sig,
nwkSeqNum, SecurityUse, timestamp, radius );
// Restore with original endpoint
aff->DstEndPoint = endpoint;
}
if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) )
{
#if !defined ( APS_NO_GROUPS )
// Find the next endpoint for this group
grpEp = aps_FindGroupForEndpoint( aff->GroupID, grpEp );
if ( grpEp == APS_GROUPS_EP_NOT_FOUND )
return; // No endpoint found
epDesc = afFindEndPointDesc( grpEp );
if ( epDesc == NULL )
return; // Endpoint descriptor not found
pList = afFindEndPointDescList( epDesc->endPoint );
#else
return;
#endif
}
else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT )
{
pList = pList->nextDesc;
if ( pList )
epDesc = pList->epDesc;
else
epDesc = NULL;
}
else
epDesc = NULL;
}
}
static void afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc,
zAddrType_t *SrcAddress, uint16 SrcPanId, NLDE_Signal_t *sig,
uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp, uint8 radius )
{
afIncomingMSGPacket_t *MSGpkt;
const uint8 len = sizeof( afIncomingMSGPacket_t ) + aff->asduLength;
uint8 *asdu = aff->asdu;
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_allocate( len );
if ( MSGpkt == NULL )
{
return;
}
MSGpkt->hdr.event = AF_INCOMING_MSG_CMD;
MSGpkt->groupId = aff->GroupID;
MSGpkt->clusterId = aff->ClusterID;
afCopyAddress( &MSGpkt->srcAddr, SrcAddress );
MSGpkt->srcAddr.endPoint = aff->SrcEndPoint;
MSGpkt->endPoint = epDesc->endPoint;
MSGpkt->wasBroadcast = aff->wasBroadcast;
MSGpkt->LinkQuality = sig->LinkQuality;
MSGpkt->correlation = sig->correlation;
MSGpkt->rssi = sig->rssi;
MSGpkt->SecurityUse = SecurityUse;
MSGpkt->timestamp = timestamp;
MSGpkt->nwkSeqNum = nwkSeqNum;
MSGpkt->macSrcAddr = aff->macSrcAddr;
MSGpkt->macDestAddr = aff->macDestAddr;
MSGpkt->srcAddr.panId = SrcPanId;
MSGpkt->cmd.TransSeqNumber = 0;
MSGpkt->cmd.DataLength = aff->asduLength;
MSGpkt->radius = radius;
if ( MSGpkt->cmd.DataLength )
{
MSGpkt->cmd.Data = (uint8 *)(MSGpkt + 1);
osal_memcpy( MSGpkt->cmd.Data, asdu, MSGpkt->cmd.DataLength );
}
else
{
MSGpkt->cmd.Data = NULL;
}
#if defined ( MT_AF_CB_FUNC )
// If ZDO or SAPI have registered for this endpoint, dont intercept it here
if (AFCB_CHECK(CB_ID_AF_DATA_IND, *(epDesc->task_id)))
{
MT_AfIncomingMsg( (void *)MSGpkt );
// Release the memory.
osal_msg_deallocate( (void *)MSGpkt );
}
else
#endif
{
// Send message through task message.
osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt );
}
}
I don't think you're decoding the frame control byte correctly. Looking at some code I've written, I interpret it as follows:
0x7c, // [0111 1100]
// 011 - reserved
// 1 - disable default response
// 1 - direction = "from server to client"
// 1 - manufacturer spec = manufacturer code present
// 00 - frame type = "Command acts across entire profile"
This is based on an old ZCL spec (around 2008?) and perhaps the reserved bits have taken on some meaning in a later version of the spec.
I believe the manufacturer specific bit indicates that this is not a standard Zigbee command (Read Attributes). If it was Read Attributes, I think it should have an even number of bytes following it (16-bit attribute IDs).
What were the source and destination endpoints, profile ID and cluster ID for that frame?
Update:
It looks like you could modify afIncomingData() to look at the fields of aff to identify this exact message type (frame control, endpoints, cluster, profile), and then hand it off to your own function for processing (and responding if necessary).
But I'd also take a look at documentation for MT_AF_CB_FUNC and MT_AfIncomingMsg() to see if that's the "official" way to identify frames that you want to handle in your own code.
In AF.c file in functions afIncomingData and afBuildMSGIncoming added bloks marked by
#if defined ( LIVOLO_SUPPORT )
#endif
In afIncomingData I add:
#if defined ( LIVOLO_SUPPORT )
else if ( aff->DstEndPoint == 0x08 )
{
if ( (epDesc = afFindEndPointDesc( 1 )) )
{
pList = afFindEndPointDescList( epDesc->endPoint );
}
}
#endif
It's prewent filtering message sended to unknown destEndpoint
And in afBuildMSGIncoming function:
#if defined ( LIVOLO_SUPPORT )
uint8 fixedPackage[] = {
0x18, 0xd8, 0x01, // header
0x00, 0x00, // attrId
0x00, // success
0x10, // boolean
0x00
};
if (aff->SrcEndPoint == 0x06 && aff->DstEndPoint == 0x01
&& aff->ClusterID == 0x0001 && aff->ProfileID == 0x0104) {
const uint8 mlfrmdHdr[] = { 0x7c, 0xd2, 0x15, 0xd8, 0x00 };
if (osal_memcmp(asdu, mlfrmdHdr, 5) == TRUE) {
fixedPackage[7] = asdu[aff->asduLength - 1];
MSGpkt->cmd.DataLength = 8; // sizeof(fixedPackage)
MSGpkt->clusterId = 0x06; // genOnOff
asdu = fixedPackage;
}
}
#endif
It change unsupported package to readAttrResp package.
Related
I have been looking for a way to convert the ObjectGuid out of Active Directory is a special type that gets converted to a string, which is odd looking, using libraries such as ldapjs and or adding it as a claim using ping federate. An example of this is the following:
const ldapjs = require("ldapjs");
let _client = ldapjs.createClient({
"url": this._ldap_uri
});
_client.search(this._search_dn, opts, (error, res) => {
res.on("searchEntry", (entry) => {
console.log(entry.object.objectGUID)
}
Here is an example of the output that comes out of ldapjs. The same comes out of a ping federate when you add it as a claim.
H�Y��fB�_-_���
However, this is equivalent to a valid UUID.
b9****48-6***-42**-a**f-2d5f*****40b
What I am trying to do is convert this strange value to a the correct UUID. I have scoured and tested a few different postings and websites but I have not found a valid solution.
Here are a few I researched:
Read objectGUID from active directory
Javascript convert GUID in string format into Base64
http://www.tdi-users.org/foswiki/Integrator/UsingGUIDs
If anyone has a solution to this it would be appreciated.
experiment with this code (read GUID) :
String.prototype.padLeft = function( len, str ) {
//return Array( len - String(this).length + 1 ).join(str) + this;
var s = this;
str = str || '0';
if ( str.length > 0 ) {
while ( s.length < len ) {
s = ( str + s );
};
}
return s;
}
var destAD = GetObject("LDAP://dc.yourdomain.pl/cn=Administrator,cn=Users,dc=yourdomain,dc=pl");
var guidValueArr = destAD.Get("objectguid").toArray();
var guid = "", i;
for ( i = 0; i < guidValueArr.length; i ++ ) {
guid += guidValueArr[i].toString(16).padLeft(2,"0");
}
var guidFormated = guid.replace(/(.{2})(.{2})(.{2})(.{2})(.{2})(.{2})(.{2})(.{2})(.{4})(.{12})/, "$4$3$2$1-$6$5-$8$7-$9-$10");
alert( guidFormated );
Universally unique identifier
I'm trying to implement a simple TCP socket server in Swift. This code should be fairly similar to a C implementation but with extra Swift-isms.
I'm trying to connect using curl http://localhost:8080 which should at least trigger an "accept" even if there is no HTTP logic in the server. The response from curl is Connection refused
Here is the code I am using. It outputs 2 "Listening on descriptor" lines, but no "Accepting…" lines
import LibC
import Dispatch
let port = 8080
let listenBacklogLen = 16
// Setup Sockets
let socketDescriptor = (
ipv4: socket(PF_INET, SOCK_STREAM, IPPROTO_TCP),
ipv6: socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)
)
let descriptors = [socketDescriptor.ipv4, socketDescriptor.ipv6]
var address = (
ipv4: sockaddr_in(),
ipv6: sockaddr_in6()
)
address.ipv4.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
address.ipv4.sin_family = sa_family_t(AF_INET)
address.ipv4.sin_port = in_port_t(port)
address.ipv4.sin_addr.s_addr = INADDR_ANY
address.ipv6.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size)
address.ipv6.sin6_family = sa_family_t(AF_INET6)
address.ipv6.sin6_port = in_port_t(port)
address.ipv6.sin6_addr = in6addr_any
var reuseAddress: Int = 1
let reuseAddressLen = socklen_t(MemoryLayout.size(ofValue: reuseAddress))
withUnsafePointer(to: &reuseAddress) { ref in
for descriptor in descriptors {
assert(setsockopt(descriptor, SOL_SOCKET, SO_REUSEADDR, ref, reuseAddressLen) == 0)
}
}
withUnsafePointer(to: &address.ipv4) { ref in
ref.withMemoryRebound(to: sockaddr.self, capacity: 1) { reboundRef in
assert(bind(socketDescriptor.ipv4, reboundRef, socklen_t(MemoryLayout.size(ofValue: ref.pointee))) == 0)
}
}
withUnsafePointer(to: &address.ipv6) { ref in
ref.withMemoryRebound(to: sockaddr.self, capacity: 1) { reboundRef in
assert(bind(socketDescriptor.ipv6, reboundRef, socklen_t(MemoryLayout.size(ofValue: ref.pointee))) == 0)
}
}
var acceptSources: [DispatchSourceRead] = []
var requestSources: [DispatchSourceRead] = []
for descriptor in descriptors {
assert(listen(descriptor, Int32(listenBacklogLen)) == 0);
let source = DispatchSource.makeReadSource(fileDescriptor: descriptor, queue: .global(qos: .userInitiated))
source.setEventHandler {
print("Accepting…")
var address = sockaddr()
var addressLen: socklen_t = socklen_t(MemoryLayout.size(ofValue: address))
let requestSocketDescriptor = accept(descriptor, &address, &addressLen)
assert(requestSocketDescriptor >= 0)
let source = DispatchSource.makeReadSource(fileDescriptor: requestSocketDescriptor, queue: .global(qos: .userInitiated))
source.setEventHandler { [unowned source] in
var char: CChar = 0
let bytesRead = read(requestSocketDescriptor, &char, 1)
switch bytesRead {
case -1:
if ![EAGAIN, EINTR].contains(errno) {
print("Read returned error (\(errno)): \(strerror(errno))")
fallthrough
}
case 0:
print("Done")
source.cancel()
default:
print("C: \(char)")
}
}
requestSources.append(source)
source.resume()
}
acceptSources.append(source)
print("Listening on descriptor: \(descriptor)")
source.resume()
}
dispatchMain()
BTW, LibC is a simple module which unifies glibc and Darwin:
#if os(Linux)
#_exported import Glibc
#else
#_exported import Darwin.C
#endif
Aha! The port must be big-endian:
address.ipv4.sin_port = in_port_t(port).bigEndian
address.ipv6.sin_port = in_port_t(port).bigEndian
Figured this out with help from: Socket Server Example with Swift
I have a client that retrieves a certificate (.pfx), including a private key, from a server and I add this to the local keychain with the following code: -
void AddCertToKeyChain(const QByteArray& cert, const QString& password)
{
SecKeychainRef keyChain = nil;
OSStatus err = SecKeychainCopyDomainDefault(kSecPreferencesDomainUser, &keyChain);
if (err != errSecSuccess)
{
emit Log("Failed to access system keychain: " + LogMessageForStatus(err));
return;
}
SecExternalFormat format = kSecFormatPKCS12;
SecExternalItemType itemType = kSecItemTypeAggregate;
SecItemImportExportFlags flags = 0;
SecItemImportExportKeyParameters params;
memset(¶ms, 0, sizeof(params));
params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
params.flags = 0;
params.passphrase = password.toCFString();
params.alertTitle = NULL;
params.alertPrompt = NULL;
params.accessRef = NULL;
// create and populate the key usage array
CFMutableArrayRef keyUsage = CFArrayCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks
);
CFArrayAppendValue(keyUsage, kSecAttrCanEncrypt);
CFArrayAppendValue(keyUsage, kSecAttrCanDecrypt);
CFArrayAppendValue(keyUsage, kSecAttrCanDerive);
CFArrayAppendValue(keyUsage, kSecAttrCanSign);
CFArrayAppendValue(keyUsage, kSecAttrCanVerify);
CFArrayAppendValue(keyUsage, kSecAttrCanWrap);
CFArrayAppendValue(keyUsage, kSecAttrCanUnwrap);
keyUsage = NULL; // Error without this - Failed to import certificate: The key usage mask is not supported.
// create and populate the key attributes array
CFMutableArrayRef keyAttributes = CFArrayCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks
);
// required for import
params.keyUsage = keyUsage;
params.keyAttributes = keyAttributes;
OSStatus status = SecItemImport(cert.toCFData(), CFSTR(".p12"), &format, &itemType, flags, ¶ms, keyChain, NULL);
if(status == errSecSuccess)
emit Log("Certificate successfully imported");
else
{
emit Log("Failed to import certificate: " + LogMessageForStatus(status));
}
}
The certificate and private key appear in the keychain, as expected.
However, trying to retrieve the certificate is a problem, either programmatically or using the Keychain application.
If I select to export the private key from the keychain, I'm provided with the following error in a dialog: -
"An error has occurred. Unable to export an item. The contents of this item cannot be retrieved"
However, if the certificate and key are added to the keychain by double-clicking on the pfx, exporting the key works as expected.
So, why would the code above cause the problem of not being able to export the key?
With the assistance of Quinn at Apple, It seems that the method described in the question should work, but doesn't.
Using an old CDSA style flag instead does in fact work, doing something like this: -
OSStatus err;
SecExternalFormat format;
SecItemImportExportKeyParameters params;
params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
params.flags = 0;
params.passphrase = (__bridge CFStringRef) pkcs12Password;
params.alertTitle = NULL;
params.alertPrompt = NULL;
params.accessRef = NULL;
params.keyUsage = NULL;
params.keyAttributes = (__bridge CFArrayRef) #[ #(CSSM_KEYATTR_EXTRACTABLE) ];
format = kSecFormatPKCS12;
err = SecItemImport(
(__bridge CFDataRef) pkcs12Data,
CFSTR("p12"),
&format,
NULL,
0,
¶ms,
keychain,
NULL
);
Note the setting of params.keyAttributes, which defines the key to be extractable.
Alternatively, the older (deprecated) SecKeychainItemImport API may be used: -
BOOL success;
OSStatus err;
NSArray * result;
SecExternalFormat format;
SecKeyImportExportParameters params;
CFArrayRef importedItems;
result = nil;
importedItems = NULL;
format = kSecFormatPKCS12;
memset(¶ms, 0, sizeof(params));
params.passphrase = password;
params.keyAttributes = CSSM_KEYATTR_EXTRACTABLE;
err = SecKeychainItemImport(
(CFDataRef) pkcs12Blob, // importedData
NULL, // fileNameOrExtension
&format, // inputFormat
NULL, // itemType
0, // flags
¶ms, // keyParams
self->keychain, // importKeychain
&importedItems // outItems
);
success = (err == noErr);
While the function SecKeychainItemImport is defined as deprecated in Apple's documentation, I have been informed that it's unlikely to be removed any time soon.
Im having a game where after I finish a certain level I put that level`s name In an Array and save that array to an SharedObject(SO). But somehow/somewhere I mess up things, because after closing and opening the game again, I load the SO data (to unlock the levels that I have passed) and it is (the loaded data) always messed up and not loading what it should load(sometimes it even trows some errors at me.)
The code is not that difficult but I have been looking at it for far to long and I need new eyes to see the obvious mistakes in it.
***********EDIT**********
So the code for the SO is this
public class LevelsData extends SharedObject
{
public static var LEVELS_DATA:SharedObject = SharedObject.getLocal("save_poi71");
public static function get LoadNamesOfLevelPassed():Array
{
var myLevelsArr:Array = [];
if( LEVELS_DATA.data.PlayedLevels_arr == undefined)
{
myLevelsArr.length = 0;
}
else
{
myLevelsArr = LEVELS_DATA.data.PlayedLevels_arr;
}
return myLevelsArr;
}
public static function set SaveLevel_Names( nameOfLevelPassed:Array ):void
{
LEVELS_DATA.data.PlayedLevels_arr = nameOfLevelPassed;
LEVELS_DATA.flush();
}
}
and the code in the game is this
.
.
.
.
private function didWePlaySameLevel():void
{
var namesPlayed_arr:Array = LevelsData.LoadNamesOfLevelPassed;
var namesTotal:int = namesPlayed_arr.length;
if( namesTotal == 0 )
{
trace(" ScreenInGame_2 | didWePlaySameLevel | namesTotal == 0 so we push Level_1");
namesPlayed_arr.push( Moving_Picture_Name );
LevelsData.SaveLevel_Names = namesPlayed_arr;
sameLevel = false;
}
else if( namesTotal != 0 )
{
for( var i:int = 0; i < namesTotal; i++)
{
trace(" ScreenInGame_2 | didWePlaySameLevel | " + Moving_Picture_Name + " ?==? " + namesPlayed_arr[ i ] );
trace(" ScreenInGame_2 | didWePlaySameLevel | namesPlayed_arr.length = " + namesPlayed_arr.length);
if( Moving_Picture_Name != namesPlayed_arr[ i ] )
{
trace(" ScreenInGame_2 | didWePlaySameLevel | " + Moving_Picture_Name + " != " + namesPlayed_arr[ i ] );
namesPlayed_arr.push( Moving_Picture_Name );
LevelsData.SaveLevel_Names = namesPlayed_arr;
sameLevel = false;
trace(" ScreenInGame_2 | didWePlaySameLevel | namesPlayed_arr.length = " + namesPlayed_arr.length);
}
}
}
}
.
.
.
if( levelsToUnlock < totalLevels && sameLevel == false )
{
trace("ScreenInGame | unlockOneMoreLevel | we UNLOCK 1 more level" );
unlockNextLevel();
}
***EDIT PROBLEM:
SomeHow i figured a way of solving my 1st problem, but now there is a new one and that is :
When the Array from the SO is != 0 i go into the else if statement. There i compare if any of the variables in the Array have the same name of the Moving_Picture_Name and if they dont then I set the sameLevel = false. The problem is that, if lets say I have passed/played 5 levels before( Level_1,Level_2,Level_3,Level_4,Level_5 ) and lets say I start playing Level_2 again it would show me this:
Level_2 != Level_1 - > and it will push itself into the Array and update the data
Level_2 == Level_2 - > it will do nothing
Level_2 != Level_3 - > and it will push itself into the Array and update the data
Level_2 != Level_4 - > and it will push itself into the Array and update the data
Level_2 != Level_5 - > and it will push itself into the Array and update the data
so it will actually push 4 Elements in the Array that exist already in that Array.
***THE QUESTION :
So how do I push the newLevel only if its name isnt in that Array(the one where I save the names), And how do I do nothing if the name exist in the Array already.
You may have to convert your array to ByteArray, and save that. When you read it back, you will have to do the reverse operation. StackOverflow example link
From Quake2 source, the function GL_BeginBuildingLightmaps at gl_rsufr.c, i saw these codes:
if ( toupper( gl_monolightmap->string[0] ) == 'A' )
{
gl_lms.internal_format = gl_tex_alpha_format;
}
/*
** try to do hacked colored lighting with a blended texture
*/
else if ( toupper( gl_monolightmap->string[0] ) == 'C' )
{
gl_lms.internal_format = gl_tex_alpha_format;
}
else if ( toupper( gl_monolightmap->string[0] ) == 'I' )
{
gl_lms.internal_format = GL_INTENSITY8;
}
else if ( toupper( gl_monolightmap->string[0] ) == 'L' )
{
gl_lms.internal_format = GL_LUMINANCE8;
}
else
{
gl_lms.internal_format = gl_tex_solid_format;
}
GL_Bind( gl_state.lightmap_textures + 0 );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
qglTexImage2D( GL_TEXTURE_2D,
0,
gl_lms.internal_format,
BLOCK_WIDTH, BLOCK_HEIGHT,
0,
GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE,
dummy );
qglTexImage2D is same as glTexImage2D.
The problem is from debugging i saw the input value of third parameter(internalFormat) of qglTexImage2D is gl_tex_solid_format, which is 3. Is 3 a valid value for parameter internalFormat?
3 is a perfectly legitimate value for internalFormat.
From the the glTexImage2D() documentation:
internalFormat: Specifies the number of color components in the texture. Must be 1, 2, 3, or 4, or one of the following symbolic constants: ...
Where is the value of the variable gl_tex_solid_format from? Are you sure you have assigned GL_RGBA to the variable gl_tex_solid_format? Maybe you assigned 3 to the variable gl_tex_solid_format.