AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Snippets and Tutorials (https://forums.alliedmods.net/forumdisplay.php?f=112)
-   -   Getting the entity list directly from any BSP file (https://forums.alliedmods.net/showthread.php?t=157474)

Thrawn2 05-21-2011 20:03

Getting the entity list directly from any BSP file
 
I've needed a way to get the entity list straight from a bsp file at runtime.
This is not beautified or anything, but what i've came up with in a hurry:
PHP Code:

#define READSIZE        4096

#define GET_LITTLE_ENDIAN_INT(%1,%2,%3)\
    
%|= %3[%1+3] & 0xFF;\
    %
<<= 8;\
    %
|= %3[%1+2] & 0xFF;\
    %
<<= 8;\
    %
|= %3[%1+1] & 0xFF;\
    %
<<= 8;\
    %
|= %3[%1] & 0xFF;

stock bool:JumpToEntityByClassname(Handle:hKV, const String:sClassName[]) {
    while (
KvGotoNextKey(hKVtrue)) {
        new 
String:sValue[255];
        
KvGetString(hKV"classname"sValuesizeof(sValue), "");

        if(
StrEqual(sValuesClassName)) {
            return 
true;
        }
    }

    return 
false;
}

stock Handle:ExtractEntities(const String:sIn[]) {
    new 
String:sMap[PLATFORM_MAX_PATH];
    
Format(sMapsizeof(sMap), "%s%s"sInStrContains(sMap".bsp"false) == -".bsp" "");

    new 
String:sPath[PLATFORM_MAX_PATH];
    
Format(sPathsizeof(sPath), "maps/%s"sMap);

    if(!
FileExists(sPath)) {
        
LogError("Map \"%s\" does not exists"sMap);
        return 
INVALID_HANDLE;
    }

    new 
String:sOutput[PLATFORM_MAX_PATH];
    
Format(sOutputsizeof(sOutput), "%s.entities.txt"sPath);

    if(!
FileExists(sOutput)) {
        
LogMessage("Grabbing entity list from %s"sMap);
        new 
Handle:hFile OpenFile(sPath"rb");

        new 
String:sFormat[5];
        
ReadFileString(hFilesFormatsizeof(sFormat), 4);

        new 
String:sVersion[5];
        
ReadFileString(hFilesVersionsizeof(sVersion), 4);
        new 
iVersion 0;
        
GET_LITTLE_ENDIAN_INT(0,iVersion,sVersion)

        
LogMessage("BSP-Format: %s"sFormat);
        if(!
StrEqual(sFormat"VBSP")) {
            
LogError("Map %s is no Valve BSP File."sMap);
            
CloseHandle(hFile);
            return 
INVALID_HANDLE;
        }
        
LogMessage("Version: %i"iVersion);

        new 
iOffsetiLengthiVersionLumpiFourCC;

        new 
String:sLump[17];
        
ReadFileString(hFilesLumpsizeof(sLump), 16);
        
ReadLump(sLumpiOffsetiLengthiVersionLumpiFourCC);

        
LogMessage("Found Entity Lump: offset %i, length %i"iOffsetiLength);

        new 
Handle:hWFile OpenFile(sOutput"w");

        new 
iNum 0;
        for(new 
iPos 0iPos iLengthiPos += READSIZE) {
            
FileSeek(hFileiOffset iPosSEEK_SET);

            new 
String:sEntities[READSIZE++READSIZE];
            
ReadFileString(hFilesEntitiessizeof(sEntities), READSIZE);

            new 
iProcessed 0;
            new 
String:sReplacement[16];

            new 
iResult 0;
            while(
iResult != -1) {
                
Format(sReplacementsizeof(sReplacement), "\"%i\"\n{"iNum);

                
iResult ReplaceStringEx(sEntities[iProcessed], sizeof(sEntities), "{"sReplacement);
                
iProcessed += iResult;
                if(
iResult != -1)iNum++;
            }

            
WriteFileString(hWFilesEntitiesfalse);
        }

        
CloseHandle(hWFile);
        
CloseHandle(hFile);
    }

    new 
Handle:hKV CreateKeyValues("");
    
FileToKeyValues(hKVsOutput);

    return 
hKV;
}

stock ReadLump(const String:sLump[17], &iOffset, &iLength, &iVersion, &iFourCC) {
    
GET_LITTLE_ENDIAN_INT(0,iOffset,sLump)
    
GET_LITTLE_ENDIAN_INT(4,iLength,sLump)
    
GET_LITTLE_ENDIAN_INT(8,iVersion,sLump)
    
GET_LITTLE_ENDIAN_INT(12,iFourCC,sLump)


This allows you to do something like this (as an example):
PHP Code:

#pragma semicolon 1
#include <sourcemod>

public OnPluginStart() {
    
RegConsoleCmd("sm_getmapmode"Command_GetMapMode);
}

public 
Action:Command_GetMapMode(client,args) {
    if(
args != 1) {
        
ReplyToCommand(client"[Usage] sm_getmapmode <mapname>[.bsp]");
        return 
Plugin_Handled;
    } else {
        new 
String:sBuffer[255];
        
GetCmdArg(1sBuffersizeof(sBuffer));

        new 
Handle:hKVEntities ExtractEntities(sBuffer);
        if(
hKVEntities == INVALID_HANDLE) {
            
ReplyToCommand(client"Map not found or wrong format");
            return 
Plugin_Handled;
        }

        new 
bool:bHasRedCapturePoints false;
        while(
JumpToEntityByClassname(hKVEntities"team_control_point")) {
            new 
iTeam KvGetNum(hKVEntities"point_default_owner"0);
            if(
iTeam == 3)bHasRedCapturePoints true;
        }

        
CloseHandle(hKVEntities);

        
ReplyToCommand(client"%s is a %s mode map"sBufferbHasRedCapturePoints "Push" "Attack/Defense");
    }

    return 
Plugin_Handled;



resulting in:
PHP Code:

[SMPlugin tExtractEntitiesFromBSP reloaded successfully.
sm_textract cp_dustbowl
cp_dustbowl is a Attack
/Defense mode map
sm_textract cp_badlands
cp_badlands is a Push mode map
sm_textract qwerty
L 05
/22/2011 01:45:07: [tExtractEntitiesFromBSP.smxMap "qwerty.bsp" does not exists
Map not found 
or wrong format
sm_textract cp_granary
L 05
/22/2011 01:45:20: [tExtractEntitiesFromBSP.smxGrabbing entity list from cp_granary.bsp
L 05
/22/2011 01:45:20: [tExtractEntitiesFromBSP.smxBSP-FormatVBSP
L 05
/22/2011 01:45:20: [tExtractEntitiesFromBSP.smxVersion20
L 05
/22/2011 01:45:20: [tExtractEntitiesFromBSP.smxFound Entity Lumpoffset 31278296length 225412
cp_granary is a Push mode map 

btw, the entity list will only be grabbed if it has not been already. The ExtractEntities stock will create a <mapname>.bsp.entities.txt in your maps folder. This should be changed to a different and dedicated folder or sth.

more information about the bsp structure can be found here.


All times are GMT -4. The time now is 21:53.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.