Moderator elections are being held. See Valve Developer Community:Moderator elections for more details.
Users who would like to run for moderator must be autoconfirmed and have at least 100 edits. Users can check their own edit count at Special:Preferences.

DEM (file format)/ru

From Valve Developer Community
Jump to: navigation, search

DEM (сокращение от ДЕМонстрации) - это формат исходного файла DEMO. Используется совместно с Source рекордером для записи демонстраций, которые содержат записанные события, которые можно редактировать и воспроизводить в игре. Демо может быть отредактирован для изменения углов камеры, ускорения, замедления, воспроизведения музыки и других функций.

Note.pngПримечание:В настоящее время нет инструментов для редактирования демонстраций после их записи.

For the purpose of this we will be referring to each collection of events as a frame. This may or may not add up to the number of frames given by the header.

Заголовок Демо

Тип Поле Значение
String Заголовок 8 символов, должно быть "HL2DEMO"+NULL
Int Демо Протокол Версия демо протокола
Int Протокол Сети Номер версии сетевого протокола
String Имя сервера Длинной в 260 символов
String Имя клиента Длинной в 260 символов
String Имя карты Длинной в 260 символов
String Директория игры Длинной в 260 символов
Float Время воспроизведения Продолжительность демонстрации в секундах
Int Тиков Количество тиков в демонстрации
Int Кадров Количество кадров в демо
Int Длина записи Длина знаков до первого кадра

Кадр

Каждый кадр начинается с 0 или более следующих команд: Они описаны в hl2sdk\utils\demofile\demoformat.h / hl2sdk-ob\public\demofile\demoformat.h / hl2sdk-l4d\public\demofile\demoformat.h

Сетевые протоколы 7 и 8

Тип Значение
dem_signon 1
dem_packet 2
dem_synctick 3
dem_consolecmd 4
dem_usercmd 5
dem_datatables 6
dem_stop 7
dem_lastcommand dem_stop

Сетевые протоколы 14 и 15

Тип Значение
dem_stringtables 8
dem_lastcommand dem_stringtables

Сетевые протоколы 36 и выше

Тип Значение
dem_customdata 8
dem_stringtables 9
dem_lastcommand dem_stringtables

В зависимости от полученной команды необходимо выполнить другое действие.

dem_stop

Это сигнал о том, что демо закончилось, и больше данных не нужно разбирать.

dem_consolecmd, dem_datatables, dem_usercmd, dem_stringtables

Чтение стандартных пакет данных, при необходимости они обработываются.

dem_synctick, dem_signon, dem_packet

Игнорирование.

Стандартные данные "Пакета"

Каждый раз, когда читается более одного байта, используется стандартный формат.

Это начинается с целого числа, которое имеет количество байтов в этом пакете.

Пример кода для этого:

int ReadData(char **buffer)
{
	int len;
	fread (&len,sizeof(int),1,fDemo);
	if (len > 0)
	{
		*buffer = new char[len];
		fread(*buffer,len,1,fDemo);
	}
	return len;
}

Формат Кадров

Frame {
       int ServerFrame;
       int ClientFrame; // ServerFrame и ClientFrame delta, вероятно, соответствуют отклику клиента.
       int SubPacketSize;
       *buffer = new char[SubPacketSize]; // Сообщение о состоянии обновлений
       Packet pkt = (rest of frame as data exists) // Все демо-команды объединены в этой области, структура ниже...
       JunkData data = (unknown) // Бывший: 0x8f 5a b5 04 94 e6 7c 24 00 00 00 00 00 ... (40 байт 0x00 после 0x24)
                                 // Это может быть либо конец кадра, либо начало следующего кадра.
}
Packet {
       char CmdType;
       int Unknown;
       int TickCount; // Это только периодический появляется.
       int SizeOfPacket;
       *buffer = new char[SizeOfPacket];
}

Пример использования

Может быть интересно захватить заголовок демонстрационных файлов на случай, если вы запустите веб-сервер, на котором размещены ваши демонстрации / телевизоры. Вот скрипты, который выполняет эту работу за вас.

PHP

<?
/* Source & OrangeBox demos header reader.
By PoLaRiTy (nocheatz.com)

Help from   : https://developer.valvesoftware.com/wiki/DEM_Format#Demo_Header
              http://hg.alliedmods.net/hl2sdks/hl2sdk-css/file/1901d5b74430/public/demofile/demoformat.h

Types sizes :
Int : 4 bytes
Float : 4 bytes
String : 260 bytes
*/


class DemoInfo_s
{
	var $dem_prot;       // Demo protocol version 
	var $net_prot;       // Network protocol versio
	var $host_name;      // HOSTNAME in case of TV, and IP:PORT or localhost:PORT in case of RIE (Record In eyes).
	var $client_name;    // Client name or TV name.
	var $map_name;       // Map name
	var $gamedir;        // Root game directory
	var $time;           // Playback time (s)
	var $ticks;          // Number of ticks
	var $frames;         // Number of frames
	var $tickrate;       // Tickrate
	var $type;           // TV or RIE ? (0 = RIE, 1 = TV)
	var $status_present; // true if a status command is available in the demo.
}

function ExtOfFile($pathtofile)
{
	return end(explode('.',$pathtofile));
}

function ReadString($handle, $n = 260)
{
	$buffer = "";
	for($d = 1; ((($char = fgetc($handle)) !== false) && ($d < $n)); $d++) $buffer = $buffer.$char;
	return trim($buffer);
}

function ReadInt($handle, $n = 4)
{
	$buf = fread($handle, $n);
	$number = unpack("i", $buf);     
	return $number[1];
}

function ReadFloat($handle)
{
	$buf = fread($handle, 4);
	$number = unpack("f", $buf);     
	return $number[1];
}

function IsGoodIPPORTFormat($string)
{
	if(preg_match('/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\:[0-9]{1,5}/', $string))	return true;
	else return false;
}

function GetDemoInfo($pathtofile, $fast = false) // BOOL fast : if true, doesn't check the presence of the status 
{
	$infos = NULL;
	if(ExtOfFile($pathtofile) === "dem")
	{
		$handle = fopen($pathtofile, "r");
		if($handle)
		{
			if(ReadString($handle, 8) === "HL2DEMO")
			{
				$infos = new DemoInfo_s;
				$infos->dem_prot = ReadInt($handle);
				$infos->net_prot = ReadInt($handle);
				$infos->host_name = ReadString($handle);
				$infos->client_name = ReadString($handle);
				$infos->map_name = ReadString($handle);
				$infos->gamedir = ReadString($handle);
				$infos->time = ReadFloat($handle);
				$infos->ticks = ReadInt($handle);
				$infos->frames = ReadInt($handle);
				$infos->tickrate = intval($infos->ticks / $infos->time);
				if(IsGoodIPPORTFormat($infos->host_name)) $infos->type = 0; // RIE   TODO : Add localhost:PORT check.
				else $infos->type = 1; // TV
				$infos->status_present = false;
				if(!$fast && !($infos->type == 1)) // No status in TV records.
				{
					while(!(($l = fgets($handle)) === false))
					{
						if(stripos($l, "\x00status\x00") !== false)
						{
							$infos->status_present = true;
							break;
						}
					}
				}
			}
			else
			{
				echo "Bad file format.";
			}
			fclose($handle);
		}
		else
		{
			echo "File not found or unable to read.";
		}
	}
	else
	{
		echo "Bad file extension.";
	}
	return $infos;
}
?>

C++

#include <Windows.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <clocale>
#include <fstream>
using namespace std;

class SourceDemo
{
private:
	struct DemoFile {
		FILE *file;
		long size;
		std::string data;
	} DemoFile;
public:
	struct DemoInfo
	{
		std::string DemoFileStamp;
		int DemoProtocol;
		int NetworkProtocol;
		std::string HostIP;
		std::string ClientName;
		std::string MapName;
		std::string GameDIR;
		double PlayBack_Time;
		int PlayBack_Ticks;
		int PlayBack_Frames;
		int SignOnLength;
		int TickRate;
		int Type;
		int Commands;
	} DemoInfo;
public:
	~SourceDemo(void);
	SourceDemo(const char* filepath);
	int ReadInt(int n = 4);
	float ReadFloat(int n = 4);
	std::string ReadString(int n = 260);
	void Release(void);
};

SourceDemo::SourceDemo(const char* filepath)
{
	this->DemoFile.file = fopen(filepath, "rb");
	this->DemoFile.size = ftell(this->DemoFile.file);
	vector<char> buf;
	fread(&buf[0], sizeof(buf[0]), buf.size(), this->DemoFile.file);
	this->DemoFile.data = string(buf.begin(), buf.end());
	//---------------------------------------------------------------//
	this->DemoInfo.DemoFileStamp = this->ReadString(8);
	if (DemoInfo.DemoFileStamp == "HL2DEMO") {
		this->DemoInfo.DemoProtocol = this->ReadInt();
		this->DemoInfo.NetworkProtocol = this->ReadInt();
		this->DemoInfo.HostIP = this->ReadString();
		this->DemoInfo.ClientName = this->ReadString();
		this->DemoInfo.MapName = this->ReadString();
		this->DemoInfo.GameDIR = this->ReadString();
		this->DemoInfo.PlayBack_Time = this->ReadFloat();
		this->DemoInfo.PlayBack_Ticks = this->ReadInt();
		this->DemoInfo.PlayBack_Frames = this->ReadInt();
		this->DemoInfo.SignOnLength = this->ReadInt();
		this->DemoInfo.TickRate = (int)(this->DemoInfo.PlayBack_Ticks / this->DemoInfo.PlayBack_Time);
		this->DemoInfo.Type = -1;
		//this->DemoInfo.Commands = static_cast<int>(this->DemoFile.data.find("\x00status\x00"));
		this->DemoInfo.Commands = -1;
	}
	else {
		this->DemoInfo.DemoProtocol = -1;
		this->DemoInfo.NetworkProtocol = -1;
		this->DemoInfo.HostIP = "NONE";
		this->DemoInfo.ClientName = "NONE";
		this->DemoInfo.MapName = "NONE";
		this->DemoInfo.GameDIR = "NONE";
		this->DemoInfo.PlayBack_Time = -1.0;
		this->DemoInfo.PlayBack_Ticks = -1;
		this->DemoInfo.PlayBack_Frames = -1;
		this->DemoInfo.TickRate = -1;
		this->DemoInfo.Type = -1;
		this->DemoInfo.Commands = -1;
	}
};
SourceDemo::~SourceDemo() {};

int SourceDemo::ReadInt(int n)
{
	std::vector<char> buf(n);
	fread(&buf[0], sizeof(buf[0]), buf.size(), this->DemoFile.file);
	std::string buffer(buf.begin(), buf.end());
	buffer.erase(std::remove(buffer.begin(), buffer.end(), '\x00'), buffer.end());
	return *((int*)&buffer.c_str()[0]);
}

float SourceDemo::ReadFloat(int n)
{
	std::vector<char> buf(n);
	fread(&buf[0], sizeof(buf[0]), buf.size(), this->DemoFile.file);
	std::string buffer(buf.begin(), buf.end());
	buffer.erase(std::remove(buffer.begin(), buffer.end(), '\x00'), buffer.end());
	return *((float*)&buffer.c_str()[0]);
}

std::string SourceDemo::ReadString(int n)
{
	std::vector<char> buf(n);
	fread(&buf[0], sizeof(buf[0]), buf.size(), this->DemoFile.file);
	std::string buffer(buf.begin(), buf.end());
	buffer.erase(std::remove(buffer.begin(), buffer.end(), '\x00'), buffer.end());
	return buffer;
}

void SourceDemo::Release(void)
{
	this->DemoInfo.DemoProtocol = -1;
	this->DemoInfo.NetworkProtocol = -1;
	this->DemoInfo.HostIP = "NONE";
	this->DemoInfo.ClientName = "NONE";
	this->DemoInfo.MapName = "NONE";
	this->DemoInfo.GameDIR = "NONE";
	this->DemoInfo.PlayBack_Time = -1.0;
	this->DemoInfo.PlayBack_Ticks = -1;
	this->DemoInfo.PlayBack_Frames = -1;
	this->DemoInfo.TickRate = -1;
	this->DemoInfo.Type = -1;
	this->DemoInfo.Commands = -1;
	fclose(this->DemoFile.file);
	return;
}

inline bool FileExist(const std::string& name) {
	ifstream f(name.c_str());
	return f.good();
}

int main(int argc, char *argv[])
{
	SetConsoleTitle(TEXT("DemoInfo"));
	SetConsoleCP(CP_UTF8);
	SetConsoleOutputCP(CP_UTF8);
	std::setlocale(LC_ALL, "en_US.UTF-8");
	std::setlocale(LC_ALL, "Russian");
	if (argc < 1) {
		char szAppPath[MAX_PATH] = "";
		GetModuleFileNameA(0, szAppPath, MAX_PATH);
		std::string AppName = szAppPath;
		AppName = AppName.substr(AppName.rfind("\\") + 1);
		std::cout << "########################################################################################" << std::endl;
		std::cout << "## Uses: " << AppName << " <demo_file_path.dem>" << std::endl;
		std::cout << "########################################################################################" << std::endl;
		Sleep(10000);
		return 0;
	}
	const char* main_executable = argv[0];
	const char* input_file = argv[1];
	if (FileExist(input_file) != false) {
		SourceDemo SD(input_file);
		std::cout << "########################################################################################" << std::endl;
		std::cout << "## Demo Info - Программа для просмотра информации у демонстрации. (OrangeBox\\Source)" << std::endl;
		std::cout << "########################################################################################" << std::endl;
		std::cout << "## File: " << input_file << std::endl;
		std::cout << "########################################################################################" << std::endl;
		std::cout << "## FileStamp\t\t-> " << SD.DemoInfo.DemoFileStamp << std::endl;
		std::cout << "## Demo Protocol\t-> " << SD.DemoInfo.DemoProtocol << std::endl;
		std::cout << "## Network Protocol\t-> " << SD.DemoInfo.NetworkProtocol << std::endl;
		std::cout << "## Host IP\t\t-> " << SD.DemoInfo.HostIP << std::endl;
		std::cout << "## Map Name\t\t-> " << SD.DemoInfo.MapName << std::endl;
		std::cout << "## GameDIR\t\t-> " << SD.DemoInfo.GameDIR << std::endl;
		std::cout << "## Time\t\t\t-> " << SD.DemoInfo.PlayBack_Time << " (Погрешность 1-2%)" << std::endl;
		std::cout << "## Ticks\t\t-> " << SD.DemoInfo.PlayBack_Ticks << std::endl;
		std::cout << "## Frames\t\t-> " << SD.DemoInfo.PlayBack_Frames << std::endl;
		std::cout << "## SignOnLength\t\t-> " << SD.DemoInfo.SignOnLength << std::endl;
		std::cout << "## TickRate\t\t-> " << SD.DemoInfo.TickRate << std::endl;
		std::cout << "## Type\t\t\t-> " << SD.DemoInfo.Type << std::endl;
		std::cout << "## Commands\t\t-> " << SD.DemoInfo.Commands << std::endl;
		std::cout << "########################################################################################" << std::endl;
		std::cout << "## Сделал Sam!" << std::endl;
		std::cout << "## https://steamcommunity.com/id/SamXDR" << std::endl;
		std::cout << "## https://steamcommunity.com/profiles/76561198312917682" << std::endl;
		std::cout << "########################################################################################" << std::endl;
		SD.Release();
		Sleep(10000);
		return 0;
	}
	else {
		char szAppPath[MAX_PATH] = "";
		GetModuleFileNameA(0, szAppPath, MAX_PATH);
		std::string AppName = szAppPath;
		AppName = AppName.substr(AppName.rfind("\\") + 1);
		std::cout << "########################################################################################" << std::endl;
		std::cout << "## Uses: " << AppName << " <demo_file_path.dem>" << std::endl;
		std::cout << "########################################################################################" << std::endl;
		Sleep(10000);
		return 0;
	}
	Sleep(10000);
	return 0;
}

Python (3.6.1)

from struct import unpack
from sys import argv
from os.path import exists
from time import sleep

class SourceDemo(object):
    __file = None
    __IsWorking = False
    DemoFileStamp = None
    DemoProtocol = -1
    NetworkProtocol = -1
    HostIP = None
    ClientName = None
    MapName = None
    GameDIR = None
    PlayBack_Time = -1.0
    PlayBack_Ticks = -1
    PlayBack_Frames = -1
    SignOnLength = -1
    TickRate = -1
    def __init__(self, file_path):
        try:
            self.__file = open(file_path, 'rb')
            self.DemoFileStamp = self.__file.read(8).replace(b'\x00', b'').decode()
            if self.DemoFileStamp == 'HL2DEMO':
                self.DemoProtocol = unpack('i', self.__file.read(4))[0]
                self.NetworkProtocol = unpack('i', self.__file.read(4))[0]
                self.HostIP = self.__file.read(260).replace(b'\x00', b'').decode()
                self.ClientName = self.__file.read(260).replace(b'\x00', b'').decode()
                self.MapName = self.__file.read(260).replace(b'\x00', b'').decode()
                self.GameDIR = self.__file.read(260).replace(b'\x00', b'').decode()
                self.PlayBack_Time = int( unpack('f', self.__file.read(4))[0] * 1000 ) / 1000
                self.PlayBack_Ticks = unpack('i', self.__file.read(4))[0]
                self.PlayBack_Frames = unpack('i', self.__file.read(4))[0]
                self.SignOnLength = unpack('i', self.__file.read(4))[0]
                self.TickRate = int(self.PlayBack_Ticks / self.PlayBack_Time)
                self.__IsWorking = True
                return
        except:
            return
    def __call__(self):
        if self.__IsWorking == True:
            if self.__IsWorking == True:
                r = {
                    'DemoFileStamp': self.DemoFileStamp,
                    'DemoProtocol': self.DemoProtocol,
                    'NetworkProtocol': self.NetworkProtocol,
                    'HostIP': self.HostIP,
                    'ClientName': self.ClientName,
                    'MapName': self.MapName,
                    'GameDIR': self.GameDIR,
                    'PlayBack_Time': self.PlayBack_Time,
                    'PlayBack_Ticks': self.PlayBack_Ticks,
                    'PlayBack_Frames': self.PlayBack_Frames,
                    'SignOnLength': self.SignOnLength,
                    'TickRate': self.TickRate,
                }
                return r
        return None
    def GetDemoFileStamp(self):
        if self.__IsWorking == True:
            return self.DemoFileStamp
        return None
    def GetDemoProtocol(self):
        if self.__IsWorking == True:
            return self.DemoProtocol
        return None
    def GetNetworkProtocol(self):
        if self.__IsWorking == True:
            return self.NetworkProtocol
        return None
    def GetHostIP(self):
        if self.__IsWorking == True:
            return self.HostIP
        return None
    def GetClientName(self):
        if self.__IsWorking == True:
            return self.ClientName
        return None
    def GetMapName(self):
        if self.__IsWorking == True:
            return self.MapName
        return None
    def GetGameDIR(self):
        if self.__IsWorking == True:
            return self.GameDIR
        return None
    def GetTime(self):
        if self.__IsWorking == True:
            return self.PlayBack_Time
        return None
    def GetTicks(self):
        if self.__IsWorking == True:
            return self.PlayBack_Ticks
        return None
    def GetFrames(self):
        if self.__IsWorking == True:
            return self.PlayBack_Frames
        return None
    def GetSignOnLength(self):
        if self.__IsWorking == True:
            return self.SignOnLength
        return None
    def GetTickRate(self):
        if self.__IsWorking == True:
            return self.TickRate
        return None

def main(argc, argv):
    file = argv[0].rsplit('\\')
    file = file[len(file)-1]
    if len(argv) == 2:
        if exists(argv[1]) == True:
            file = argv[1].rsplit('\\')
            file = file[len(file)-1]
            s = SourceDemo(argv[1])
            print('########################################################################################')
            print('## DemoInfo.py - Скрипт для просмотра информации у демонстрации. (OrangeBox\\Source)')
            print('########################################################################################')
            print('## File: {}'.format(file))
            print('########################################################################################')
            print('## FileStamp\t\t-> {}'.format(s.GetDemoFileStamp()))
            print('## Demo Protocol\t-> {}'.format(s.GetDemoProtocol()))
            print('## Network Protocol\t-> {}'.format(s.GetNetworkProtocol()))
            print('## Host IP\t\t-> {}'.format(s.GetHostIP()))
            print('## Map Name\t\t-> {}'.format(s.GetMapName()))
            print('## GameDIR\t\t-> {}'.format(s.GetGameDIR()))
            print('## Time\t\t\t-> {} (Имеется погрешность!)'.format(s.GetTime()))
            print('## Ticks\t\t-> {}'.format(s.GetTicks()))
            print('## Frames\t\t-> {}'.format(s.GetFrames()))
            print('## SignOnLength\t\t-> {}'.format(s.GetSignOnLength()))
            print('## TickRate\t\t-> {}'.format(s.GetTickRate()))
            print('########################################################################################')
            return
    print('########################################################################################')
    print('## Uses: {} <demo_file_path.dem>'.format(file))
    print('########################################################################################')
    return

if __name__ == '__main__':
    argc = len(argv)
    argv = argv
    main(argc, argv)
    sleep(25)
    exit(0)

Смотрите также