|
|
|
|
@ -0,0 +1,170 @@
|
|
|
|
|
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
|
|
|
|
|
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
|
|
|
|
|
#include "Recompiler.hpp"
|
|
|
|
|
#include "VulkanTestDevice.hpp"
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <bit>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
using AgcDriver::Graphics::Require;
|
|
|
|
|
using ShaderRecompiler::ShaderStage;
|
|
|
|
|
|
|
|
|
|
constexpr std::uint32_t Threads = 32;
|
|
|
|
|
constexpr std::uint32_t Inputs = 4;
|
|
|
|
|
constexpr std::uint32_t Results = 16;
|
|
|
|
|
alignas(256) std::array<std::uint32_t, Threads * Inputs> Input{};
|
|
|
|
|
alignas(256) std::array<std::uint32_t, Threads * Results> Output{};
|
|
|
|
|
|
|
|
|
|
alignas(256) constexpr std::array<std::uint32_t, 50> Code{
|
|
|
|
|
0x34020084, 0x34060086, 0xe0381000, 0x80000401, 0xbf8c3f70, 0xd572000a, 0x041a0d04, 0xd573000c,
|
|
|
|
|
0x041a0d04, 0xd575000e, 0x04120d04, 0xd5720012, 0x02020906, 0xd5750014, 0x04120b06, 0xd5730018,
|
|
|
|
|
0x04120b06, 0xe0701000, 0x80010a03, 0xe0701004, 0x80010b03, 0xe0701008, 0x80010c03, 0xe070100c,
|
|
|
|
|
0x80010d03, 0xe0701010, 0x80010e03, 0xe0701014, 0x80010f03, 0xe0701018, 0x80011003, 0xe070101c,
|
|
|
|
|
0x80011103, 0xe0701020, 0x80011203, 0xe0701024, 0x80011303, 0xe0701028, 0x80011403, 0xe070102c,
|
|
|
|
|
0x80011503, 0xe0701030, 0x80011603, 0xe0701034, 0x80011703, 0xe0701038, 0x80011803, 0xe070103c,
|
|
|
|
|
0x80011903, 0xbf810000,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constexpr std::uint32_t Rows[32][4] = {
|
|
|
|
|
{0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u},
|
|
|
|
|
{0xffffffffu, 0xffffffffu, 0x00000000u, 0x00000000u},
|
|
|
|
|
{0x00000000u, 0x00000000u, 0xffffffffu, 0xffffffffu},
|
|
|
|
|
{0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu},
|
|
|
|
|
{0x01020304u, 0x05060708u, 0x00ff00ffu, 0xfffefffdu},
|
|
|
|
|
{0xff00ff00u, 0x00ff00ffu, 0x00000000u, 0xffffffffu},
|
|
|
|
|
{0x12345678u, 0x9abcdef0u, 0x00010203u, 0xffff0000u},
|
|
|
|
|
{0xffffffffu, 0x00000000u, 0x00ff0000u, 0xfffffff0u},
|
|
|
|
|
{0x80808080u, 0x7f7f7f7fu, 0x01000001u, 0x00010000u},
|
|
|
|
|
{0xfffffff0u, 0xfffffff0u, 0xffffffffu, 0xffff0001u},
|
|
|
|
|
{0xecc3f80cu, 0xc7859faeu, 0xf2d7d40fu, 0x4a37fa2du},
|
|
|
|
|
{0xe47682e6u, 0xd46375dcu, 0x156393d8u, 0x045f21dau},
|
|
|
|
|
{0x978f18a7u, 0x4e86c4fau, 0x6c7ab5c9u, 0x611244c0u},
|
|
|
|
|
{0x87b3d90eu, 0x5bab1eecu, 0x215b8892u, 0xb9d8249eu},
|
|
|
|
|
{0x3126b9c3u, 0x44732494u, 0x71cf92e3u, 0x039a7b88u},
|
|
|
|
|
{0x389605feu, 0x9ec2d776u, 0x747d0a2bu, 0xe6d30f0au},
|
|
|
|
|
{0x065720ceu, 0x1b1c3f27u, 0x15f54a5cu, 0xfb34ccc5u},
|
|
|
|
|
{0xb5c318e9u, 0x7f8cb6d1u, 0x6bd6eed6u, 0x05032a7eu},
|
|
|
|
|
{0x833bfa03u, 0xa0aeb4e5u, 0xbbf5204au, 0x6e6944d3u},
|
|
|
|
|
{0x5be72f6eu, 0xc55176d5u, 0x0ebbe4d0u, 0x34a3f451u},
|
|
|
|
|
{0xc8796571u, 0x090a0e01u, 0x96d0cbffu, 0x5e7db530u},
|
|
|
|
|
{0xa86cf7b4u, 0x2c03a513u, 0x9ef94bdau, 0x374ebe5au},
|
|
|
|
|
{0xf8821a96u, 0xbc36c297u, 0x5cdff86du, 0x90c68e93u},
|
|
|
|
|
{0xc2816017u, 0xdc3260fdu, 0x5e620fe9u, 0xf7f3a683u},
|
|
|
|
|
{0x9271a86fu, 0x4801920eu, 0xa918a1dcu, 0xf9810e12u},
|
|
|
|
|
{0x507bef95u, 0x92af2f03u, 0x15f2cd71u, 0xd0745cc1u},
|
|
|
|
|
{0x795c6ad3u, 0x2d794610u, 0xb7c93640u, 0x7bab67bcu},
|
|
|
|
|
{0x97323268u, 0xca9f211eu, 0x728c2f7bu, 0xacfc4403u},
|
|
|
|
|
{0xa472ea5fu, 0x29e0b1f5u, 0x2e99777fu, 0xcc8ba3e3u},
|
|
|
|
|
{0x2db4cb47u, 0x8899230bu, 0x52cb26d8u, 0xac61e3a6u},
|
|
|
|
|
{0xd0dd15d6u, 0x85232575u, 0x32135b34u, 0x951179a0u},
|
|
|
|
|
{0x1d42d810u, 0x15136429u, 0x193e1ff7u, 0x6d17abfbu}
|
|
|
|
|
};
|
|
|
|
|
constexpr std::uint32_t Expected[32][16] = {
|
|
|
|
|
{0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u},
|
|
|
|
|
{0x03fc03fcu, 0x03fc03fcu, 0x00000000u, 0x00000000u, 0xffffffffu, 0xffffffffu, 0x00000000u, 0x00000000u, 0x03fc03fcu, 0x03fc03fcu, 0x000003fbu, 0x000003fbu, 0x000003fcu, 0x000003fcu, 0x03fb03fbu, 0x03fb03fbu},
|
|
|
|
|
{0x03fb03fbu, 0x03fb03fbu, 0x03fb03fbu, 0x03fb03fbu, 0x000003fcu, 0x000003fcu, 0x000003fbu, 0x000003fbu, 0x03fc03fcu, 0x03fc03fcu, 0x00000000u, 0x00000000u, 0xffffffffu, 0xffffffffu, 0x00000000u, 0x00000000u},
|
|
|
|
|
{0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0x00000000u, 0x00000000u, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu},
|
|
|
|
|
{0x030302fbu, 0x020201f9u, 0x02f902f7u, 0x01f401f1u, 0x010204fcu, 0x05060902u, 0x00ff02f3u, 0xffff01f3u, 0x01fe01fcu, 0x02f802f7u, 0x01020500u, 0x05060906u, 0x00ff03eeu, 0xffff02edu, 0x03000500u, 0x07f609f7u},
|
|
|
|
|
{0x02fd01feu, 0x02fc01fdu, 0x00000000u, 0xffffffffu, 0xff00ff00u, 0x00ff00ffu, 0x00000000u, 0xffffffffu, 0x00ff01feu, 0x00ff01feu, 0xff0100feu, 0x00ff02fdu, 0x000000ffu, 0x000000feu, 0x00fe00feu, 0x01fe01feu},
|
|
|
|
|
{0x01870311u, 0x0295020eu, 0x009702ffu, 0x01d90130u, 0x12345774u, 0x9abcdf86u, 0x00010333u, 0xffff01dau, 0x0111010eu, 0x01ef0113u, 0x12345996u, 0x9abce211u, 0x00010526u, 0xffff02efu, 0x15555996u, 0x9dabe213u},
|
|
|
|
|
{0x02fd02fdu, 0x01fd02edu, 0x00ff0000u, 0x00fe00efu, 0xffffffffu, 0x00000000u, 0x00ff00ffu, 0x000000efu, 0x020d02fdu, 0x010e010eu, 0xffffffffu, 0x00000000u, 0x00ff0000u, 0xfffffff0u, 0xffffffffu, 0x00000000u},
|
|
|
|
|
{0x02fd01ffu, 0x01fc01fcu, 0x01fd00ffu, 0x00fe00fdu, 0x8080817eu, 0x7f7f807cu, 0x010000feu, 0x000100fdu, 0x01ff01feu, 0x01fe01ffu, 0x8080827au, 0x7f7f817au, 0x010001fcu, 0x000101fau, 0x827b827au, 0x8179817au},
|
|
|
|
|
{0x000e000eu, 0x000e0010u, 0x000e000eu, 0x000e0010u, 0xffffffffu, 0xffffffffu, 0x0000000eu, 0xffff0010u, 0x010d000fu, 0x020c020cu, 0xffffffffu, 0x000000fdu, 0x0000020bu, 0xffff020du, 0x010cffffu, 0x020b01fcu},
|
|
|
|
|
{0xf42ad450u, 0x4bdffb75u, 0xf42ad450u, 0x4bdffb75u, 0xecc3f84du, 0xc785a101u, 0xf2d7d557u, 0x4a37fbd5u, 0x01d70041u, 0x029d0175u, 0xecc3f95du, 0xc785a113u, 0xf2d7d516u, 0x4a37fbe8u, 0xee28f95du, 0xc940a0b5u},
|
|
|
|
|
{0x171e94d9u, 0x05142366u, 0x171e94d9u, 0x05142366u, 0xe47683e7u, 0xd4637797u, 0x15639564u, 0x045f228fu, 0x00dd0101u, 0x02030217u, 0xe47683c7u, 0xd463768bu, 0x156395dbu, 0x045f23bdu, 0xe52583c7u, 0xd64677dfu},
|
|
|
|
|
{0x6dfcb6c8u, 0x61ed45f0u, 0x6dfcb6c8u, 0x61ed45f0u, 0x978f19a6u, 0x4e86c67cu, 0x6c7ab6f9u, 0x6112459bu, 0x00bc00ffu, 0x01b30105u, 0x978f1911u, 0x4e86c615u, 0x6c7ab6e5u, 0x611245d0u, 0x98aa1911u, 0x4f96c616u},
|
|
|
|
|
{0x22c48a25u, 0xbb0e2554u, 0x22c48a25u, 0xbb0e2554u, 0x87b3daa1u, 0x5bab2055u, 0x215b8948u, 0xb9d825d4u, 0x01a10193u, 0x012e017du, 0x87b3da5cu, 0x5bab205au, 0x215b896au, 0xb9d826edu, 0x8921da5cu, 0x5dfa1fc4u},
|
|
|
|
|
{0x73269413u, 0x04fb7d2eu, 0x73269413u, 0x04fb7d2eu, 0x3126baf3u, 0x447325ebu, 0x71cf9489u, 0x039a7ce9u, 0x00e90130u, 0x01410100u, 0x3126bb09u, 0x44732587u, 0x71cf93b7u, 0x039a7c6du, 0x3219bb09u, 0x45582568u},
|
|
|
|
|
{0x75760b58u, 0xe7f4100du, 0x75760b58u, 0xe7f4100du, 0x3896072bu, 0x9ec2d86fu, 0x747d0b2eu, 0xe6d3102bu, 0x01bc012du, 0x01b101a5u, 0x38960785u, 0x9ec2d91eu, 0x747d0bdcu, 0xe6d310c1u, 0x3a3e0785u, 0xa079d927u},
|
|
|
|
|
{0x173f4ba5u, 0xfc6ace06u, 0x173f4ba5u, 0xfc6ace06u, 0x06572217u, 0x1b1c4071u, 0x15f54b9du, 0xfb34cdfbu, 0x025a0149u, 0x02010166u, 0x065721edu, 0x1b1c40b1u, 0x15f54caeu, 0xfb34ce26u, 0x07e121edu, 0x1c7d4179u},
|
|
|
|
|
{0x6d46f01cu, 0x05822b1au, 0x6d46f01cu, 0x05822b1au, 0xb5c31a2fu, 0x7f8cb841u, 0x6bd6ef72u, 0x05032afdu, 0x01520146u, 0x022f0136u, 0xb5c31984u, 0x7f8cb730u, 0x6bd6ef89u, 0x05032bfau, 0xb6221984u, 0x8108b784u},
|
|
|
|
|
{0xbd5c225du, 0x6fb5455cu, 0xbd5c225du, 0x6fb5455cu, 0x833bfc16u, 0xa0aeb64cu, 0xbbf520d3u, 0x6e69461fu, 0x00f20213u, 0x01020208u, 0x833bfb94u, 0xa0aeb62bu, 0xbbf520e2u, 0x6e6945bdu, 0x8481fb94u, 0xa198b57du},
|
|
|
|
|
{0x1086e660u, 0x35aff573u, 0x1086e660u, 0x35aff573u, 0x5be730feu, 0xc55178a0u, 0x0ebbe5f2u, 0x34a3f55du, 0x01e50190u, 0x00d7019du, 0x5be73102u, 0xc55177e0u, 0x0ebbe581u, 0x34a3f602u, 0x5cf23102u, 0xc7027786u},
|
|
|
|
|
{0x9859cd7cu, 0x60ccb710u, 0x9859cd7cu, 0x60ccb710u, 0xc87966eeu, 0x090a0f8au, 0x96d0cddfu, 0x5e7db77fu, 0x017a017du, 0x00e100ecu, 0xc879687fu, 0x090a1040u, 0x96d0ce28u, 0x5e7db706u, 0xcab8687fu, 0x0ae0102au},
|
|
|
|
|
{0xa0134d43u, 0x38a7c012u, 0xa0134d43u, 0x38a7c012u, 0xa86cf91du, 0x2c03a62du, 0x9ef94d92u, 0x374ebfb3u, 0x00eb0169u, 0x015f00c6u, 0xa86cfa3du, 0x2c03a668u, 0x9ef94db0u, 0x374ec00du, 0xa9c1fa3du, 0x2db6a6e9u},
|
|
|
|
|
{0x5dfcfa6du, 0x91f58f56u, 0x5dfcfa6du, 0x91f58f56u, 0xf8821c96u, 0xbc36c3b4u, 0x5cdff930u, 0x90c68fc2u, 0x01b20200u, 0x00f10106u, 0xf8821bffu, 0xbc36c364u, 0x5cdff9a6u, 0x90c68f5fu, 0xf94f1bffu, 0xbd02c3d0u},
|
|
|
|
|
{0x605c118fu, 0xf936a83bu, 0x605c118fu, 0xf936a83bu, 0xc28161bdu, 0xdc3262f7u, 0x5e6211a1u, 0xf7f3a7c6u, 0x006c01a6u, 0x00c0006bu, 0xc281612au, 0xdc326272u, 0x5e62110du, 0xf7f3a7d0u, 0xc3f6612au, 0xdd7f6221u},
|
|
|
|
|
{0xaa91a2c0u, 0xfb800eadu, 0xaa91a2c0u, 0xfb800eadu, 0x9271a953u, 0x48019387u, 0xa918a277u, 0xf9811011u, 0x017a00e4u, 0x0144013bu, 0x9271a9c4u, 0x480193f9u, 0xa918a248u, 0xf9810f73u, 0x945ca9c4u, 0x4962927au},
|
|
|
|
|
{0x1776ce69u, 0xd2bc5e51u, 0x1776ce69u, 0xd2bc5e51u, 0x507bf08du, 0x92af3087u, 0x15f2cf01u, 0xd0745f09u, 0x011200f8u, 0x00f10189u, 0x507bf161u, 0x92af3159u, 0x15f2cec2u, 0xd0745dd6u, 0x52d1f161u, 0x93c43054u},
|
|
|
|
|
{0xb91037b2u, 0x7ccb6945u, 0xb91037b2u, 0x7ccb6945u, 0x795c6c45u, 0x2d794757u, 0xb7c937c9u, 0x7bab68dcu, 0x019a0172u, 0x00ab00c9u, 0x795c6bedu, 0x2d794786u, 0xb7c937e7u, 0x7bab6969u, 0x7ad26bedu, 0x2f2647b7u},
|
|
|
|
|
{0x73373010u, 0xadc14573u, 0x73373010u, 0xadc14573u, 0x973232fdu, 0xca9f21c9u, 0x728c30ebu, 0xacfc44c8u, 0x01670095u, 0x00b000e6u, 0x9732333eu, 0xca9f228eu, 0x728c315cu, 0xacfc4502u, 0x98a2333eu, 0xcb9e22ffu},
|
|
|
|
|
{0x2fdb78afu, 0xcdf8a4fcu, 0x2fdb78afu, 0xcdf8a4fcu, 0xa472eb8fu, 0x29e0b337u, 0x2e997898u, 0xcc8ba550u, 0x00ec0130u, 0x00820168u, 0xa472eb5bu, 0x29e0b3f7u, 0x2e9978dbu, 0xcc8ba57bu, 0xa674eb5bu, 0x2b78b351u},
|
|
|
|
|
{0x544b284au, 0xae16e4c0u, 0x544b284au, 0xae16e4c0u, 0x2db4ccb9u, 0x8899248bu, 0x52cb27f2u, 0xac61e55bu, 0x00fc0172u, 0x009301c1u, 0x2db4cc7fu, 0x88992433u, 0x52cb282fu, 0xac61e4e1u, 0x2edccc7fu, 0x89d42462u},
|
|
|
|
|
{0x33b45d84u, 0x95e87b2du, 0x33b45d84u, 0x95e87b2du, 0xd0dd1826u, 0x85232716u, 0x32135cc1u, 0x95117a77u, 0x01580250u, 0x02520174u, 0xd0dd16b0u, 0x852325cbu, 0x32135c2cu, 0x95117b28u, 0xd13316b0u, 0x86ab266du},
|
|
|
|
|
{0x19b1219fu, 0x6e27ad12u, 0x19b1219fu, 0x6e27ad12u, 0x1d42d9b8u, 0x1513649cu, 0x193e210eu, 0x6d17ad0bu, 0x01b001a8u, 0x009b0234u, 0x1d42d952u, 0x15136545u, 0x193e21d5u, 0x6d17ad3cu, 0x1e5ed952u, 0x16546607u}
|
|
|
|
|
};
|
|
|
|
|
constexpr const char* Names[16] = {"qsad lo", "qsad hi", "mqsad_pk lo", "mqsad_pk hi", "mqsad_u32 0", "mqsad_u32 1", "mqsad_u32 2", "mqsad_u32 3", "qsad const lo", "qsad const hi", "mqsad_u32 b0", "mqsad_u32 b1", "mqsad_u32 b2", "mqsad_u32 b3", "mqsad_pk b lo", "mqsad_pk b hi"};
|
|
|
|
|
|
|
|
|
|
void Fill(std::uint32_t tid, std::uint32_t* words) {
|
|
|
|
|
std::copy(std::begin(Rows[tid]), std::end(Rows[tid]), words);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
|
|
|
|
|
const auto address = reinterpret_cast<std::uintptr_t>(data);
|
|
|
|
|
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), bytes, 0x01016facu};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string Hex(std::uint32_t value) {
|
|
|
|
|
char text[16];
|
|
|
|
|
std::snprintf(text, sizeof(text), "0x%08x", value);
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Expect(std::uint32_t tid, std::uint32_t actual, std::uint32_t expected, const char* name) {
|
|
|
|
|
Require(actual == expected, std::string("vop3 quad sad: lane ") + std::to_string(tid) + " " + name + " is " + Hex(actual) + ", expected " + Hex(expected));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Run(AgcDriver::VulkanDevice& device) {
|
|
|
|
|
for (std::uint32_t tid = 0; tid < Threads; ++tid) Fill(tid, &Input[tid * Inputs]);
|
|
|
|
|
Output.fill(0xdeadbeefu);
|
|
|
|
|
std::vector<std::uint32_t> userData(8, 0u);
|
|
|
|
|
const auto input = BufferDescriptor(Input.data(), static_cast<std::uint32_t>(Input.size() * 4u));
|
|
|
|
|
const auto output = BufferDescriptor(Output.data(), static_cast<std::uint32_t>(Output.size() * 4u));
|
|
|
|
|
std::copy(input.begin(), input.end(), userData.begin());
|
|
|
|
|
std::copy(output.begin(), output.end(), userData.begin() + 4);
|
|
|
|
|
const std::span<const std::uint32_t> code(Code);
|
|
|
|
|
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
|
|
|
|
|
const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1};
|
|
|
|
|
ShaderRecompiler::RecompileRequest request{
|
|
|
|
|
{ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}},
|
|
|
|
|
{32, 0, userData, compute, std::nullopt, std::nullopt, memory},
|
|
|
|
|
device.Target(),
|
|
|
|
|
{0, 0, 0, 128}
|
|
|
|
|
};
|
|
|
|
|
request.useCache = false;
|
|
|
|
|
const auto result = ShaderRecompiler::Recompile(request);
|
|
|
|
|
device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(code.data()));
|
|
|
|
|
device.WaitIdle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Check() {
|
|
|
|
|
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
|
|
|
|
|
const std::uint32_t* in = &Input[tid * Inputs];
|
|
|
|
|
const std::uint32_t* out = &Output[tid * Results];
|
|
|
|
|
for (std::uint32_t i = 0; i < 16; ++i) Expect(tid, out[i], Expected[tid][i], Names[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
try {
|
|
|
|
|
const auto device = OpenVulkanTestDevice();
|
|
|
|
|
if (!device) return VulkanTestSkipped;
|
|
|
|
|
Run(*device);
|
|
|
|
|
Check();
|
|
|
|
|
std::puts("vop3 quad sad tests passed");
|
|
|
|
|
return 0;
|
|
|
|
|
} catch (const std::exception& error) {
|
|
|
|
|
std::cerr << error.what() << '\n';
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|