Пример #1
0
TEST(StringTests, rlong)
{
    LString long_text =
        "01234567890123456789012345678901234567890123456789"
        "0123456789012345678901234567890123456789012345 100"
        "01234567890123456789012345678901234567890123456789"
        "0123456789012345678901234567890123456789012345 200"
        "01234567890123456789012345678901234567890123456789"
        "0123456789012345678901234567890123456789012345 300"_s;
    RString r = long_text;
    EXPECT_EQ(r.size(), 300);
    AString a = long_text;
    EXPECT_EQ(r, a);
    AString r2 = r, r3;
    RString a2 = a, a3;
    XString r1 = r2;
    XString a1 = a2;
    r3 = r1;
    a3 = a1;
    EXPECT_EQ(r, r1);
    EXPECT_EQ(a, a1);
    EXPECT_EQ(r, r2);
    EXPECT_EQ(a, a2);
    EXPECT_EQ(r, r3);
    EXPECT_EQ(a, a3);
    EXPECT_EQ(&*r.begin(), &*r1.begin());
    EXPECT_EQ(&*a.begin(), &*a1.begin());
    EXPECT_EQ(&*r.begin(), &*r2.begin());
    EXPECT_EQ(&*a.begin(), &*a2.begin());
    EXPECT_EQ(&*r.begin(), &*r3.begin());
    EXPECT_EQ(&*a.begin(), &*a3.begin());
}
Пример #2
0
TEST(StringTests, rshort)
{
    LString short_text = "0123456789"_s;
    EXPECT_EQ(&*short_text.begin(), &*RString(short_text).begin());
    EXPECT_EQ(&*short_text.begin(), &*AString(short_text).begin());
    RString r = VString<255>(short_text);
    EXPECT_EQ(r.size(), 10);
    AString a = VString<255>(short_text);
    EXPECT_EQ(r, a);
    AString r2 = r, r3;
    RString a2 = a, a3;
    XString r1 = r2;
    XString a1 = a2;
    r3 = r1;
    a3 = a1;
    EXPECT_EQ(r, r1);
    EXPECT_EQ(a, a1);
    EXPECT_EQ(r, r2);
    EXPECT_EQ(a, a2);
    EXPECT_EQ(r, r3);
    EXPECT_EQ(a, a3);
    EXPECT_EQ(&*r.begin(), &*r1.begin());
    EXPECT_NE(&*a.begin(), &*a1.begin());
    EXPECT_EQ(&*r.begin(), &*r2.begin());
    EXPECT_NE(&*a.begin(), &*a2.begin());
    EXPECT_EQ(&*r.begin(), &*r3.begin());
    EXPECT_NE(&*a.begin(), &*a3.begin());
}
Пример #3
0
TEST(StringTests, rempty)
{
    LString empty_text = ""_s;
    RString r = empty_text;
    EXPECT_EQ(r.size(), 0);
    AString a = empty_text;
    EXPECT_EQ(r, a);
    AString r2 = r, r3;
    RString a2 = a, a3;
    XString r1 = r2;
    XString a1 = a2;
    r3 = r1;
    a3 = a1;
    EXPECT_EQ(r, r1);
    EXPECT_EQ(a, a1);
    EXPECT_EQ(r, r2);
    EXPECT_EQ(a, a2);
    EXPECT_EQ(r, r3);
    EXPECT_EQ(a, a3);
    EXPECT_EQ(&*r.begin(), &*r1.begin());
    EXPECT_EQ(&*a.begin(), &*a1.begin());
    EXPECT_EQ(&*r.begin(), &*r2.begin());
    EXPECT_EQ(&*a.begin(), &*a2.begin());
    EXPECT_EQ(&*r.begin(), &*r3.begin());
    EXPECT_EQ(&*a.begin(), &*a3.begin());
}
Пример #4
0
void InFtpAnalyzerStream::ProcessLine(const AString& line) {
	FtpAnalyzer *fa = (FtpAnalyzer*)m_analyzer;
	cmatch m;
	if (regex_search((const char*)line.begin(), (const char*)line.end(), m, s_reFtpResponse)) {
		int code = atoi(String(m[1]));
		switch (code)
		{
		case 220:
			{
				ASCIIEncoding enc;
				String s = enc.GetChars(line);
				vector<String> vec = s.Split();
				if (vec.size() >= 2 && vec[1].Find(".") != -1)
					fa->ServerName = vec[1];
			}
			break;
		case 227:
			fa->PrepareEndpoint(ASCIIEncoding().GetChars(line));
			break;
		case 230:
			if (fa->UserName!="anonymous" && !fa->m_user)
				(fa->m_user=FtpUser::FindByServerLogin(fa->m_ci->DstEndPoint, fa->UserName))->SetPassword(fa->Password);
			break;
		}
	}
}
Пример #5
0
void OutFtpAnalyzerStream::ProcessLine(const AString& line) {
	FtpAnalyzer *fa = (FtpAnalyzer*)m_analyzer;
	cmatch m;
	if (regex_search((const char*)line.begin(), (const char*)line.end(), m, s_reFtpCommand)) {
		String cmd = m[1];
		String arg = m[3];
		if (cmd == "USER")
			fa->UserName = arg;
		else if (cmd == "PASS")
			fa->Password = arg;
		else if (cmd == "RETR" || cmd == "STOR") {
			ptr<FtpFileMessage> fm = new FtpFileMessage;
			fm->m_analyzerClass = &g_ftpMessageAnalyzerClass;
			fm->ClientAddress = fa->m_ci->SrcEndPoint.Address;
			fm->From = WebUser::GetByClientAddress(fm->ClientAddress);
			String servername = !!fa->ServerName ? fa->ServerName : fa->m_ci->DstEndPoint.Address.ToString();
			fm->Text = "ftp://"+servername+arg;
			fm->Filename = arg;
			fm->Finish();
			fa->TrackMessage(fm.get());
		} else if (cmd == "PORT") {
			fa->PrepareEndpoint(arg);
		}
	} else
		fa->Delete();
}
Пример #6
0
void cBrewingRecipes::ReloadRecipes(void)
{
	ClearRecipes();
	LOGD("Loading brewing recipes...");

	std::ifstream f(BREWING_RECIPE_FILE, std::ios::in);
	if (!f.good())
	{
		LOG("Could not open the brewing recipes file \"%s\". No brewing recipes are available.", BREWING_RECIPE_FILE);
		return;
	}

	unsigned int LineNum = 0;
	AString ParsingLine;

	while (std::getline(f, ParsingLine))
	{
		LineNum++;
		// Remove comments from the line:
		size_t FirstCommentSymbol = ParsingLine.find('#');
		if (FirstCommentSymbol != AString::npos)
		{
			ParsingLine.erase(ParsingLine.begin() += static_cast<long>(FirstCommentSymbol), ParsingLine.end());
		}

		if (ParsingLine.empty())
		{
			continue;
		}
		AddRecipeFromLine(ParsingLine, LineNum);
	}  // while (getline(ParsingLine))

	LOG("Loaded " SIZE_T_FMT " brewing recipes", m_pState->Recipes.size());
}
Пример #7
0
void cFurnaceRecipe::ReloadRecipes(void)
{
	ClearRecipes();
	LOGD("Loading furnace recipes...");

	std::ifstream f(FURNACE_RECIPE_FILE, std::ios::in);
	if (!f.good())
	{
		LOG("Could not open the furnace recipes file \"%s\". No furnace recipes are available.", FURNACE_RECIPE_FILE);
		return;
	}

	unsigned int LineNum = 0;
	AString ParsingLine;

	while (std::getline(f, ParsingLine))
	{
		LineNum++;
		if (ParsingLine.empty())
		{
			continue;
		}

		// Remove comments from the line:
		size_t FirstCommentSymbol = ParsingLine.find('#');
		if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0))
		{
			ParsingLine.erase(ParsingLine.begin() + static_cast<const long>(FirstCommentSymbol), ParsingLine.end());
		}

		switch (ParsingLine[0])
		{
			case '#':
			{
				// Comment
				break;
			}

			case '!':
			{
				AddFuelFromLine(ParsingLine, LineNum);
				break;
			}

			default:
			{
				AddRecipeFromLine(ParsingLine, LineNum);
				break;
			}
		}  // switch (ParsingLine[0])
	}  // while (getline(ParsingLine))

	LOG("Loaded " SIZE_T_FMT " furnace recipes and " SIZE_T_FMT " fuels", m_pState->Recipes.size(), m_pState->Fuel.size());
}
Пример #8
0
void cPlayer::KilledBy(cEntity * a_Killer)
{
	super::KilledBy(a_Killer);

	if (m_Health > 0)
	{
		return; //  not dead yet =]
	}

	m_bVisible = false; // So new clients don't see the player

	// Puke out all the items
	cItems Pickups;
	m_Inventory.CopyToItems(Pickups);
	m_Inventory.Clear();

	if (GetName() == "Notch")
	{
		Pickups.Add(cItem(E_ITEM_RED_APPLE));
	}

	m_Stats.AddValue(statItemsDropped, Pickups.Size());

	m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
	SaveToDisk();  // Save it, yeah the world is a tough place !

	if (a_Killer == NULL)
	{
		GetWorld()->BroadcastChatDeath(Printf("%s was killed by environmental damage", GetName().c_str()));
	}
	else if (a_Killer->IsPlayer())
	{
		cPlayer * Killer = (cPlayer *)a_Killer;

		GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str()));
	}
	else
	{
		AString KillerClass = a_Killer->GetClass();
		KillerClass.erase(KillerClass.begin()); // Erase the 'c' of the class (e.g. "cWitch" -> "Witch")

		GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str()));
	}

	m_Stats.AddValue(statDeaths);

	m_World->GetScoreBoard().AddPlayerScore(GetName(), cObjective::otDeathCount, 1);
}
Пример #9
0
void cWSSCompact::cPAKFile::UpdateChunk2To3()
{
	int Offset = 0;
	AString NewDataContents;
	int ChunksConverted = 0;
	for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
	{
		sChunkHeader * Header = *itr;

		if( ChunksConverted % 32 == 0 )
		{
			LOGINFO("Updating \"%s\" version 2 to version 3: " SIZE_T_FMT  " %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() );
		}
		ChunksConverted++;

		AString Data;
		int UncompressedSize = Header->m_UncompressedSize;
		Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
		Offset += Header->m_CompressedSize;

		// Crude data integrity check:
		const int ExpectedSize = (16*256*16)*2 + (16*256*16)/2; // For version 2
		if (UncompressedSize < ExpectedSize)
		{
			LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
				Header->m_ChunkX, Header->m_ChunkZ,
				UncompressedSize, ExpectedSize
				);
			Offset += Header->m_CompressedSize;
			continue;
		}

		// Decompress the data:
		AString UncompressedData;
		{
			int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize);
			if (errorcode != Z_OK)
			{
				LOGERROR("Error %d decompressing data for chunk [%d, %d]", 
					errorcode,
					Header->m_ChunkX, Header->m_ChunkZ
					);
				Offset += Header->m_CompressedSize;
				continue;
			}
		}

		if (UncompressedSize != (int)UncompressedData.size())
		{
			LOGWARNING("Uncompressed data size differs (exp %d bytes, got " SIZE_T_FMT  ") for chunk [%d, %d]",
				UncompressedSize, UncompressedData.size(),
				Header->m_ChunkX, Header->m_ChunkZ
				);
			Offset += Header->m_CompressedSize;
			continue;
		}

		char ConvertedData[ExpectedSize];
		memset(ConvertedData, 0, ExpectedSize);

		// Cannot use cChunk::MakeIndex because it might change again?????????
		// For compatibility, use what we know is current
		#define MAKE_3_INDEX( x, y, z ) ( x + (z * 16) + (y * 16 * 16) )

		unsigned int InChunkOffset = 0;
		for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) // YZX Loop order is important, in 1.1 Y was first then Z then X
		{
			ConvertedData[ MAKE_3_INDEX(x, y, z) ] = UncompressedData[InChunkOffset];
			++InChunkOffset;
		}  // for y, z, x

		
		unsigned int index2 = 0;
		for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
		{
			ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
			++index2;
		}
		InChunkOffset += index2 / 2;
		index2 = 0;

		for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
		{
			ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
			++index2;
		}
		InChunkOffset += index2 / 2;
		index2 = 0;

		for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
		{
			ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
			++index2;
		}
		InChunkOffset += index2 / 2;

		AString Converted(ConvertedData, ExpectedSize);

		// Add JSON data afterwards
		if (UncompressedData.size() > InChunkOffset)
		{
			Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() );
		}

		// Re-compress data
		AString CompressedData;
		{
			int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData, m_CompressionFactor);
			if (errorcode != Z_OK)
			{
				LOGERROR("Error %d compressing data for chunk [%d, %d]", 
					errorcode,
					Header->m_ChunkX, Header->m_ChunkZ
					);
				continue;
			}
		}

		// Save into file's cache
		Header->m_UncompressedSize = Converted.size();
		Header->m_CompressedSize = CompressedData.size();
		NewDataContents.append( CompressedData );
	}

	// Done converting
	m_DataContents = NewDataContents;
	m_ChunkVersion = 3;
	SynchronizeFile();

	LOGINFO("Updated \"%s\" version 2 to version 3", m_FileName.c_str() );
}
Пример #10
0
void cWSSCompact::cPAKFile::UpdateChunk1To2()
{
	int Offset = 0;
	AString NewDataContents;
	int ChunksConverted = 0;
	for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
	{
		sChunkHeader * Header = *itr;

		if( ChunksConverted % 32 == 0 )
		{
			LOGINFO("Updating \"%s\" version 1 to version 2: " SIZE_T_FMT  " %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() );
		}
		ChunksConverted++;

		AString Data;
		int UncompressedSize = Header->m_UncompressedSize;
		Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
		Offset += Header->m_CompressedSize;

		// Crude data integrity check:
		int ExpectedSize = (16*128*16)*2 + (16*128*16)/2; // For version 1
		if (UncompressedSize < ExpectedSize)
		{
			LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
				Header->m_ChunkX, Header->m_ChunkZ,
				UncompressedSize, ExpectedSize
				);
			Offset += Header->m_CompressedSize;
			continue;
		}

		// Decompress the data:
		AString UncompressedData;
		{
			int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize);
			if (errorcode != Z_OK)
			{
				LOGERROR("Error %d decompressing data for chunk [%d, %d]", 
					errorcode,
					Header->m_ChunkX, Header->m_ChunkZ
					);
				Offset += Header->m_CompressedSize;
				continue;
			}
		}

		if (UncompressedSize != (int)UncompressedData.size())
		{
			LOGWARNING("Uncompressed data size differs (exp %d bytes, got " SIZE_T_FMT  ") for chunk [%d, %d]",
				UncompressedSize, UncompressedData.size(),
				Header->m_ChunkX, Header->m_ChunkZ
				);
			Offset += Header->m_CompressedSize;
			continue;
		}


		// Old version is 128 blocks high with YZX axis order
		char ConvertedData[cChunkDef::BlockDataSize];
		int Index = 0;
		unsigned int InChunkOffset = 0;
		for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) 
		{
			for( int y = 0; y < 128; ++y )
			{
				ConvertedData[Index++] = UncompressedData[y + z * 128 + x * 128 * 16 + InChunkOffset];
			}
			// Add 128 empty blocks after an old y column
			memset(ConvertedData + Index, E_BLOCK_AIR, 128);
			Index += 128;
		}
		InChunkOffset += (16 * 128 * 16);
		for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Metadata
		{
			for( int y = 0; y < 64; ++y )
			{
				ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
			}
			memset(ConvertedData + Index, 0, 64);
			Index += 64;
		}
		InChunkOffset += (16 * 128 * 16) / 2;
		for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Block light
		{
			for( int y = 0; y < 64; ++y )
			{
				ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
			}
			memset(ConvertedData + Index, 0, 64);
			Index += 64;
		}
		InChunkOffset += (16*128*16)/2;
		for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Sky light
		{
			for( int y = 0; y < 64; ++y )
			{
				ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
			}
			memset(ConvertedData + Index, 0, 64);
			Index += 64;
		}
		InChunkOffset += (16 * 128 * 16) / 2;

		AString Converted(ConvertedData, ARRAYCOUNT(ConvertedData));
		
		// Add JSON data afterwards
		if (UncompressedData.size() > InChunkOffset)
		{
			Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() );
		}

		// Re-compress data
		AString CompressedData;
		{
			int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData,m_CompressionFactor);
			if (errorcode != Z_OK)
			{
				LOGERROR("Error %d compressing data for chunk [%d, %d]", 
					errorcode,
					Header->m_ChunkX, Header->m_ChunkZ
				);
				continue;
			}
		}

		// Save into file's cache
		Header->m_UncompressedSize = Converted.size();
		Header->m_CompressedSize = CompressedData.size();
		NewDataContents.append( CompressedData );
	}

	// Done converting
	m_DataContents = NewDataContents;
	m_ChunkVersion = 2;
	SynchronizeFile();
	
	LOGINFO("Updated \"%s\" version 1 to version 2", m_FileName.c_str() );
}
Пример #11
0
void cPlayer::KilledBy(TakeDamageInfo & a_TDI)
{
	super::KilledBy(a_TDI);

	if (m_Health > 0)
	{
		return;  //  not dead yet =]
	}

	m_bVisible = false;  // So new clients don't see the player

	// Puke out all the items
	cItems Pickups;
	m_Inventory.CopyToItems(Pickups);
	m_Inventory.Clear();

	if (GetName() == "Notch")
	{
		Pickups.Add(cItem(E_ITEM_RED_APPLE));
	}

	m_Stats.AddValue(statItemsDropped, (StatValue)Pickups.Size());

	m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
	SaveToDisk();  // Save it, yeah the world is a tough place !

	if ((a_TDI.Attacker == NULL) && m_World->ShouldBroadcastDeathMessages())
	{
		AString DamageText;
		switch (a_TDI.DamageType)
		{
			case dtRangedAttack: DamageText = "was shot"; break;
			case dtLightning: DamageText = "was plasmified by lightining"; break;
			case dtFalling: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "fell to death" : "hit the ground too hard"; break;
			case dtDrowning: DamageText = "drowned"; break;
			case dtSuffocating: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "git merge'd into a block" : "fused with a block"; break;
			case dtStarving: DamageText = "forgot the importance of food"; break;
			case dtCactusContact: DamageText = "was impaled on a cactus"; break;
			case dtLavaContact: DamageText = "was melted by lava"; break;
			case dtPoisoning: DamageText = "died from septicaemia"; break;
			case dtWithering: DamageText = "is a husk of their former selves"; break;
			case dtOnFire: DamageText = "forgot to stop, drop, and roll"; break;
			case dtFireContact: DamageText = "burnt themselves to death"; break;
			case dtInVoid: DamageText = "somehow fell out of the world"; break;
			case dtPotionOfHarming: DamageText = "was magicked to death"; break;
			case dtEnderPearl: DamageText = "misused an ender pearl"; break;
			case dtAdmin: DamageText = "was administrator'd"; break;
			case dtExplosion: DamageText = "blew up"; break;
			default: DamageText = "died, somehow; we've no idea how though"; break;
		}
		GetWorld()->BroadcastChatDeath(Printf("%s %s", GetName().c_str(), DamageText.c_str()));
	}
	else if (a_TDI.Attacker == NULL)  // && !m_World->ShouldBroadcastDeathMessages() by fallthrough
	{
		// no-op
	}
	else if (a_TDI.Attacker->IsPlayer())
	{
		cPlayer * Killer = (cPlayer *)a_TDI.Attacker;

		GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str()));
	}
	else
	{
		AString KillerClass = a_TDI.Attacker->GetClass();
		KillerClass.erase(KillerClass.begin());  // Erase the 'c' of the class (e.g. "cWitch" -> "Witch")

		GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str()));
	}

	m_Stats.AddValue(statDeaths);

	m_World->GetScoreBoard().AddPlayerScore(GetName(), cObjective::otDeathCount, 1);
}
Пример #12
0
bool itemdb_readdb(ZString filename)
{
    bool rv = true;

    int ln = 0, lines = 0;

    {
        io::ReadFile in(filename);

        if (!in.is_open())
        {
            PRINTF("can't read %s\n"_fmt, filename);
            return false;
        }

        lines = 0;

        AString line;
        while (in.getline(line))
        {
            lines++;
            if (is_comment(line))
                continue;
            // a line is 17 normal fields followed by 2 {} fields
            // the fields are separated by ", *", but there may be ,
            // in the {}.

            auto it = std::find(line.begin(), line.end(), '{');
            XString main_part = line.xislice_h(it).rstrip();
            // According to the code, tail_part may be empty. See later.
            ZString tail_part = line.xislice_t(it);

            XString unused_slot_count;
            item_data idv {};
            if (!extract(
                        main_part, record<','>(
                            &idv.nameid,
                            lstripping(&idv.name),
                            lstripping(&idv.jname),
                            lstripping(&idv.type),
                            lstripping(&idv.value_buy),
                            lstripping(&idv.value_sell),
                            lstripping(&idv.weight),
                            lstripping(&idv.atk),
                            lstripping(&idv.def),
                            lstripping(&idv.range),
                            lstripping(&idv.magic_bonus),
                            lstripping(&unused_slot_count),
                            lstripping(&idv.sex),
                            lstripping(&idv.equip),
                            lstripping(&idv.wlv),
                            lstripping(&idv.elv),
                            lstripping(&idv.look)
                        )
                    )
            )
            {
                PRINTF("%s:%d: error: bad item line: %s\n"_fmt,
                        filename, lines, line);
                rv = false;
                continue;
            }

            ln++;

            Borrowed<struct item_data> id = itemdb_search(idv.nameid);
            *id = std::move(idv);
            if (id->value_buy == 0 && id->value_sell == 0)
            {
            }
            else if (id->value_buy == 0)
            {
                id->value_buy = id->value_sell * 2;
            }
            else if (id->value_sell == 0)
            {
                id->value_sell = id->value_buy / 2;
            }

            id->use_script = nullptr;
            id->equip_script = nullptr;

            if (!tail_part)
                continue;
            id->use_script = parse_script(tail_part, lines, true);

            tail_part = tail_part.xislice_t(std::find(tail_part.begin() + 1, tail_part.end(), '{'));
            if (!tail_part)
                continue;
            id->equip_script = parse_script(tail_part, lines, true);
        }
        PRINTF("read %s done (count=%d)\n"_fmt, filename, ln);
    }

    return rv;
}