mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-06-15 09:17:57 -05:00
Reformat code with clang format
This commit is contained in:
@ -13,4 +13,4 @@ const std::string& IPakLoadException::DetailedMessage() const
|
||||
char const* IPakLoadException::what() const noexcept
|
||||
{
|
||||
return "There was an error when trying to load an ipak file.";
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
#include "IPak.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
|
||||
#include "Exception/IPakLoadException.h"
|
||||
#include "IPakStreamManager.h"
|
||||
#include "ObjContainer/IPak/IPakTypes.h"
|
||||
#include "Utils/FileUtils.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "Utils/FileUtils.h"
|
||||
#include "Exception/IPakLoadException.h"
|
||||
#include "ObjContainer/IPak/IPakTypes.h"
|
||||
#include "IPakStreamManager.h"
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@ -57,7 +56,8 @@ class IPak::Impl : public ObjContainerReferenceable
|
||||
m_index_entries.push_back(indexEntry);
|
||||
}
|
||||
|
||||
std::sort(m_index_entries.begin(), m_index_entries.end(),
|
||||
std::sort(m_index_entries.begin(),
|
||||
m_index_entries.end(),
|
||||
[](const IPakIndexEntry& entry1, const IPakIndexEntry& entry2)
|
||||
{
|
||||
return entry1.key.combinedKey < entry2.key.combinedKey;
|
||||
@ -152,8 +152,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~Impl() override
|
||||
= default;
|
||||
~Impl() override = default;
|
||||
|
||||
std::string GetName() override
|
||||
{
|
||||
|
@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <istream>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "ObjContainer/ObjContainerReferenceable.h"
|
||||
#include "ObjContainer/ObjContainerRepository.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/ObjStream.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
class IPak final : public ObjContainerReferenceable
|
||||
{
|
||||
class Impl;
|
||||
|
@ -1,16 +1,15 @@
|
||||
#include "IPakEntryReadStream.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include <minilzo.h>
|
||||
|
||||
#include "ObjContainer/IPak/IPakTypes.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <minilzo.h>
|
||||
|
||||
using namespace ipak_consts;
|
||||
|
||||
IPakEntryReadStream::IPakEntryReadStream(std::istream& stream, IPakStreamManagerActions* streamManagerActions,
|
||||
uint8_t* chunkBuffer, const int64_t startOffset, const size_t entrySize)
|
||||
IPakEntryReadStream::IPakEntryReadStream(
|
||||
std::istream& stream, IPakStreamManagerActions* streamManagerActions, uint8_t* chunkBuffer, const int64_t startOffset, const size_t entrySize)
|
||||
: m_chunk_buffer(chunkBuffer),
|
||||
m_stream(stream),
|
||||
m_stream_manager_actions(streamManagerActions),
|
||||
@ -85,8 +84,8 @@ bool IPakEntryReadStream::SetChunkBufferWindow(const int64_t startPos, size_t ch
|
||||
// Check whether we need to load additional data that was not previously loaded
|
||||
if (endPos > m_buffer_end_pos)
|
||||
{
|
||||
const auto readChunkCount = ReadChunks(&m_chunk_buffer[m_buffer_end_pos - startPos], m_buffer_end_pos,
|
||||
static_cast<size_t>(endPos - m_buffer_end_pos) / IPAK_CHUNK_SIZE);
|
||||
const auto readChunkCount =
|
||||
ReadChunks(&m_chunk_buffer[m_buffer_end_pos - startPos], m_buffer_end_pos, static_cast<size_t>(endPos - m_buffer_end_pos) / IPAK_CHUNK_SIZE);
|
||||
|
||||
m_buffer_end_pos += static_cast<int64_t>(readChunkCount) * IPAK_CHUNK_SIZE;
|
||||
|
||||
@ -100,20 +99,18 @@ bool IPakEntryReadStream::SetChunkBufferWindow(const int64_t startPos, size_t ch
|
||||
// Check whether the end position is already part of the loaded data
|
||||
if (endPos > m_buffer_start_pos && endPos <= m_buffer_end_pos)
|
||||
{
|
||||
assert(IPAK_CHUNK_SIZE * IPAK_CHUNK_COUNT_PER_READ - static_cast<size_t>(m_buffer_start_pos - startPos) >= static_cast<size_t>(endPos - m_buffer_start_pos));
|
||||
assert(IPAK_CHUNK_SIZE * IPAK_CHUNK_COUNT_PER_READ - static_cast<size_t>(m_buffer_start_pos - startPos)
|
||||
>= static_cast<size_t>(endPos - m_buffer_start_pos));
|
||||
|
||||
// Move data to make sure the end is at the appropriate position to be able to load the missing data in the front
|
||||
memmove(&m_chunk_buffer[m_buffer_start_pos - startPos], m_chunk_buffer, static_cast<size_t>(endPos - m_buffer_start_pos));
|
||||
|
||||
// We already established that the start of the buffer is not already loaded so we will need to load additional data nonetheless
|
||||
const auto readChunkCount = ReadChunks(m_chunk_buffer,
|
||||
startPos,
|
||||
static_cast<size_t>(m_buffer_start_pos - startPos) / IPAK_CHUNK_SIZE);
|
||||
const auto readChunkCount = ReadChunks(m_chunk_buffer, startPos, static_cast<size_t>(m_buffer_start_pos - startPos) / IPAK_CHUNK_SIZE);
|
||||
|
||||
m_buffer_start_pos = startPos;
|
||||
m_buffer_end_pos = readChunkCount == (m_buffer_start_pos - startPos) / IPAK_CHUNK_SIZE
|
||||
? endPos
|
||||
: startPos + static_cast<int64_t>(readChunkCount) * IPAK_CHUNK_SIZE;
|
||||
m_buffer_end_pos =
|
||||
readChunkCount == (m_buffer_start_pos - startPos) / IPAK_CHUNK_SIZE ? endPos : startPos + static_cast<int64_t>(readChunkCount) * IPAK_CHUNK_SIZE;
|
||||
|
||||
return m_buffer_end_pos == endPos;
|
||||
}
|
||||
@ -143,8 +140,7 @@ bool IPakEntryReadStream::ValidateBlockHeader(const IPakDataBlockHeader* blockHe
|
||||
{
|
||||
// If compressed is not 0 or 1 it will not be read and therefore it is okay when the offset does not match
|
||||
// The game uses IPAK_COMMAND_SKIP as value for compressed when it intends to skip the specified amount of data
|
||||
if (blockHeader->commands[currentCommand].compressed == 0
|
||||
|| blockHeader->commands[currentCommand].compressed == 1)
|
||||
if (blockHeader->commands[currentCommand].compressed == 0 || blockHeader->commands[currentCommand].compressed == 1)
|
||||
{
|
||||
std::cerr << "IPak block offset (" << blockHeader->countAndOffset.offset << ") is not the file head (" << m_file_head << ") -> Invalid\n";
|
||||
return false;
|
||||
@ -192,8 +188,7 @@ bool IPakEntryReadStream::NextBlock()
|
||||
const auto chunkStartPos = AlignBackwards<int64_t>(m_pos, IPAK_CHUNK_SIZE);
|
||||
const auto blockOffsetInChunk = static_cast<size_t>(m_pos - chunkStartPos);
|
||||
|
||||
auto estimatedChunksToRead = AlignForward(m_entry_size - static_cast<size_t>(m_pos - m_base_pos), IPAK_CHUNK_SIZE)
|
||||
/ IPAK_CHUNK_SIZE;
|
||||
auto estimatedChunksToRead = AlignForward(m_entry_size - static_cast<size_t>(m_pos - m_base_pos), IPAK_CHUNK_SIZE) / IPAK_CHUNK_SIZE;
|
||||
|
||||
if (estimatedChunksToRead > IPAK_CHUNK_COUNT_PER_READ)
|
||||
estimatedChunksToRead = IPAK_CHUNK_COUNT_PER_READ;
|
||||
@ -222,8 +217,7 @@ bool IPakEntryReadStream::ProcessCommand(const size_t commandSize, const int com
|
||||
if (compressed == 1)
|
||||
{
|
||||
lzo_uint outputSize = sizeof(m_decompress_buffer);
|
||||
const auto result = lzo1x_decompress_safe(&m_chunk_buffer[m_pos - m_buffer_start_pos], commandSize,
|
||||
m_decompress_buffer, &outputSize, nullptr);
|
||||
const auto result = lzo1x_decompress_safe(&m_chunk_buffer[m_pos - m_buffer_start_pos], commandSize, m_decompress_buffer, &outputSize, nullptr);
|
||||
|
||||
if (result != LZO_E_OK)
|
||||
{
|
||||
@ -261,8 +255,7 @@ bool IPakEntryReadStream::AdvanceStream()
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessCommand(m_current_block->commands[m_next_command].size,
|
||||
m_current_block->commands[m_next_command].compressed);
|
||||
ProcessCommand(m_current_block->commands[m_next_command].size, m_current_block->commands[m_next_command].compressed);
|
||||
m_next_command++;
|
||||
|
||||
return true;
|
||||
|
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <istream>
|
||||
|
||||
#include "Utils/ObjStream.h"
|
||||
#include "IPakStreamManager.h"
|
||||
#include "ObjContainer/IPak/IPakTypes.h"
|
||||
#include "Utils/ObjStream.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
class IPakEntryReadStream final : public objbuf
|
||||
{
|
||||
@ -33,14 +33,12 @@ class IPakEntryReadStream final : public objbuf
|
||||
int64_t m_buffer_start_pos;
|
||||
int64_t m_buffer_end_pos;
|
||||
|
||||
template <typename T>
|
||||
static T AlignForward(const T num, const T alignTo)
|
||||
template<typename T> static T AlignForward(const T num, const T alignTo)
|
||||
{
|
||||
return (num + alignTo - 1) / alignTo * alignTo;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T AlignBackwards(const T num, const T alignTo)
|
||||
template<typename T> static T AlignBackwards(const T num, const T alignTo)
|
||||
{
|
||||
return num / alignTo * alignTo;
|
||||
}
|
||||
@ -86,8 +84,8 @@ class IPakEntryReadStream final : public objbuf
|
||||
/**
|
||||
* \brief Processes a command with the specified parameters at the current position.
|
||||
* \param commandSize The size of the command data
|
||||
* \param compressed The compression value of the command. Can be \c 0 for uncompressed or \c 1 for lzo compression. Any other value skips the specified size of data.
|
||||
* \return \c true if the specified command could be correctly processed or \c otherwise.
|
||||
* \param compressed The compression value of the command. Can be \c 0 for uncompressed or \c 1 for lzo compression. Any other value skips the specified
|
||||
* size of data. \return \c true if the specified command could be correctly processed or \c otherwise.
|
||||
*/
|
||||
bool ProcessCommand(size_t commandSize, int compressed);
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
#include "IPakStreamManager.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "IPakEntryReadStream.h"
|
||||
#include "ObjContainer/IPak/IPakTypes.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
using namespace ipak_consts;
|
||||
|
||||
class IPakStreamManager::Impl final : public IPakStreamManagerActions
|
||||
@ -71,10 +71,12 @@ public:
|
||||
m_stream_mutex.lock();
|
||||
|
||||
ChunkBuffer* reservedChunkBuffer;
|
||||
const auto freeChunkBuffer = std::find_if(m_chunk_buffers.begin(), m_chunk_buffers.end(), [](ChunkBuffer* chunkBuffer)
|
||||
{
|
||||
return chunkBuffer->m_using_stream == nullptr;
|
||||
});
|
||||
const auto freeChunkBuffer = std::find_if(m_chunk_buffers.begin(),
|
||||
m_chunk_buffers.end(),
|
||||
[](ChunkBuffer* chunkBuffer)
|
||||
{
|
||||
return chunkBuffer->m_using_stream == nullptr;
|
||||
});
|
||||
|
||||
if (freeChunkBuffer == m_chunk_buffers.end())
|
||||
{
|
||||
@ -109,10 +111,12 @@ public:
|
||||
{
|
||||
m_stream_mutex.lock();
|
||||
|
||||
const auto openStreamEntry = std::find_if(m_open_streams.begin(), m_open_streams.end(), [stream](const ManagedStream& managedStream)
|
||||
{
|
||||
return managedStream.m_stream == stream;
|
||||
});
|
||||
const auto openStreamEntry = std::find_if(m_open_streams.begin(),
|
||||
m_open_streams.end(),
|
||||
[stream](const ManagedStream& managedStream)
|
||||
{
|
||||
return managedStream.m_stream == stream;
|
||||
});
|
||||
|
||||
if (openStreamEntry != m_open_streams.end())
|
||||
{
|
||||
|
@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <istream>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/ObjStream.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <istream>
|
||||
#include <mutex>
|
||||
|
||||
class IPakStreamManagerActions
|
||||
{
|
||||
public:
|
||||
@ -31,4 +31,4 @@ public:
|
||||
IPakStreamManager& operator=(IPakStreamManager&& other) noexcept = delete;
|
||||
|
||||
_NODISCARD std::unique_ptr<iobjstream> OpenStream(int64_t startPosition, size_t length) const;
|
||||
};
|
||||
};
|
||||
|
@ -3,12 +3,12 @@
|
||||
#include "ObjLoading.h"
|
||||
#include "Utils/FileToZlibWrapper.h"
|
||||
|
||||
#include <unzip.h>
|
||||
#include <filesystem>
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unzip.h>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ObjContainer/ObjContainerRepository.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/ObjStream.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "ObjContainer/ObjContainerRepository.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class IWD final : public ISearchPath, IObjContainer
|
||||
{
|
||||
@ -33,4 +33,4 @@ public:
|
||||
std::string GetPath() override;
|
||||
std::string GetName() override;
|
||||
void Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) override;
|
||||
};
|
||||
};
|
||||
|
@ -4,13 +4,12 @@
|
||||
#include "Utils/TransformIterator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
template <typename ContainerType, typename ReferencerType>
|
||||
class ObjContainerRepository
|
||||
template<typename ContainerType, typename ReferencerType> class ObjContainerRepository
|
||||
{
|
||||
class ObjContainerEntry
|
||||
{
|
||||
@ -49,10 +48,12 @@ public:
|
||||
|
||||
bool AddContainerReference(ContainerType* container, ReferencerType* referencer)
|
||||
{
|
||||
auto firstEntry = std::find_if(m_containers.begin(), m_containers.end(), [container](const ObjContainerEntry& entry)
|
||||
{
|
||||
return entry.m_container.get() == container;
|
||||
});
|
||||
auto firstEntry = std::find_if(m_containers.begin(),
|
||||
m_containers.end(),
|
||||
[container](const ObjContainerEntry& entry)
|
||||
{
|
||||
return entry.m_container.get() == container;
|
||||
});
|
||||
|
||||
if (firstEntry != m_containers.end())
|
||||
{
|
||||
@ -87,10 +88,12 @@ public:
|
||||
|
||||
ContainerType* GetContainerByName(const std::string& name)
|
||||
{
|
||||
auto foundEntry = std::find_if(m_containers.begin(), m_containers.end(), [name](ObjContainerEntry& entry)
|
||||
{
|
||||
return entry.m_container->GetName() == name;
|
||||
});
|
||||
auto foundEntry = std::find_if(m_containers.begin(),
|
||||
m_containers.end(),
|
||||
[name](ObjContainerEntry& entry)
|
||||
{
|
||||
return entry.m_container->GetName() == name;
|
||||
});
|
||||
|
||||
if (foundEntry != m_containers.end())
|
||||
{
|
||||
@ -102,13 +105,19 @@ public:
|
||||
|
||||
TransformIterator<typename std::vector<ObjContainerEntry>::iterator, ObjContainerEntry&, ContainerType*> begin()
|
||||
{
|
||||
return TransformIterator<typename std::vector<ObjContainerEntry>::iterator, ObjContainerEntry&, ContainerType*>(
|
||||
m_containers.begin(), [](ObjContainerEntry& entry) { return entry.m_container.get(); });
|
||||
return TransformIterator<typename std::vector<ObjContainerEntry>::iterator, ObjContainerEntry&, ContainerType*>(m_containers.begin(),
|
||||
[](ObjContainerEntry& entry)
|
||||
{
|
||||
return entry.m_container.get();
|
||||
});
|
||||
}
|
||||
|
||||
TransformIterator<typename std::vector<ObjContainerEntry>::iterator, ObjContainerEntry&, ContainerType*> end()
|
||||
{
|
||||
return TransformIterator<typename std::vector<ObjContainerEntry>::iterator, ObjContainerEntry&, ContainerType*>(
|
||||
m_containers.end(), [](ObjContainerEntry& entry){ return entry.m_container.get(); });
|
||||
return TransformIterator<typename std::vector<ObjContainerEntry>::iterator, ObjContainerEntry&, ContainerType*>(m_containers.end(),
|
||||
[](ObjContainerEntry& entry)
|
||||
{
|
||||
return entry.m_container.get();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1,13 +1,12 @@
|
||||
#include "SoundBank.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
#include "Utils/FileUtils.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "Utils/FileUtils.h"
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
ObjContainerRepository<SoundBank, Zone> SoundBank::Repository;
|
||||
|
||||
@ -149,7 +148,8 @@ bool SoundBank::ReadHeader()
|
||||
|
||||
if (m_header.entrySize != sizeof(SoundAssetBankEntry))
|
||||
{
|
||||
std::cout << "Invalid sndbank entry size 0x" << std::hex << m_header.entrySize << " (should be 0x" << std::hex << sizeof(SoundAssetBankEntry) << ")" << std::endl;
|
||||
std::cout << "Invalid sndbank entry size 0x" << std::hex << m_header.entrySize << " (should be 0x" << std::hex << sizeof(SoundAssetBankEntry) << ")"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -159,8 +159,7 @@ bool SoundBank::ReadHeader()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_header.entryCount
|
||||
&& (m_header.entryOffset <= 0 || m_header.entryOffset + sizeof(SoundAssetBankEntry) * m_header.entryCount > m_file_size))
|
||||
if (m_header.entryCount && (m_header.entryOffset <= 0 || m_header.entryOffset + sizeof(SoundAssetBankEntry) * m_header.entryCount > m_file_size))
|
||||
{
|
||||
std::cout << "Invalid sndbank entry offset " << m_header.entryOffset << " (filesize is " << m_file_size << ")" << std::endl;
|
||||
return false;
|
||||
@ -280,9 +279,7 @@ bool SoundBank::Initialize()
|
||||
if (m_initialized)
|
||||
return true;
|
||||
|
||||
if (!ReadHeader()
|
||||
|| !ReadEntries()
|
||||
|| !ReadChecksums())
|
||||
if (!ReadHeader() || !ReadEntries() || !ReadChecksums())
|
||||
return false;
|
||||
|
||||
m_initialized = true;
|
||||
|
@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <istream>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "ObjContainer/ObjContainerReferenceable.h"
|
||||
#include "ObjContainer/ObjContainerRepository.h"
|
||||
#include "ObjContainer/SoundBank/SoundBankTypes.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/FileUtils.h"
|
||||
#include "Utils/ObjStream.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
class SoundBankEntryInputStream
|
||||
{
|
||||
public:
|
||||
|
@ -17,19 +17,19 @@ struct SoundAssetBankChecksum
|
||||
|
||||
struct SoundAssetBankHeader
|
||||
{
|
||||
unsigned int magic; // + 0x0
|
||||
unsigned int version; // + 0x4
|
||||
unsigned int entrySize; // + 0x8
|
||||
unsigned int checksumSize; // + 0xC
|
||||
unsigned int dependencySize; // + 0x10
|
||||
unsigned int entryCount; // + 0x14
|
||||
unsigned int dependencyCount; // + 0x18
|
||||
unsigned int pad32; // + 0x1C
|
||||
int64_t fileSize; // + 0x20
|
||||
int64_t entryOffset; // + 0x28
|
||||
int64_t checksumOffset; // + 0x30
|
||||
SoundAssetBankChecksum checksumChecksum; // + 0x38
|
||||
char dependencies[512]; // + 0x48
|
||||
unsigned int magic; // + 0x0
|
||||
unsigned int version; // + 0x4
|
||||
unsigned int entrySize; // + 0x8
|
||||
unsigned int checksumSize; // + 0xC
|
||||
unsigned int dependencySize; // + 0x10
|
||||
unsigned int entryCount; // + 0x14
|
||||
unsigned int dependencyCount; // + 0x18
|
||||
unsigned int pad32; // + 0x1C
|
||||
int64_t fileSize; // + 0x20
|
||||
int64_t entryOffset; // + 0x28
|
||||
int64_t checksumOffset; // + 0x30
|
||||
SoundAssetBankChecksum checksumChecksum; // + 0x38
|
||||
char dependencies[512]; // + 0x48
|
||||
};
|
||||
|
||||
struct SoundAssetBankEntry
|
||||
|
Reference in New Issue
Block a user