1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3
4#include "filecollection.h"
5
6#include <base/math.h>
7#include <base/str.h>
8#include <base/time.h>
9
10#include <engine/storage.h>
11
12#include <algorithm>
13
14void CFileCollection::Init(IStorage *pStorage, const char *pPath, const char *pFileDesc, const char *pFileExt, int MaxEntries)
15{
16 m_vFileEntries.clear();
17 str_copy(dst&: m_aFileDesc, src: pFileDesc);
18 m_FileDescLength = str_length(str: m_aFileDesc);
19 str_copy(dst&: m_aFileExt, src: pFileExt);
20 m_FileExtLength = str_length(str: m_aFileExt);
21 str_copy(dst&: m_aPath, src: pPath);
22 m_pStorage = pStorage;
23
24 m_pStorage->ListDirectory(Type: IStorage::TYPE_SAVE, pPath: m_aPath, pfnCallback: FilelistCallback, pUser: this);
25 std::sort(first: m_vFileEntries.begin(), last: m_vFileEntries.end(), comp: [](const CFileEntry &Lhs, const CFileEntry &Rhs) { return Lhs.m_Timestamp < Rhs.m_Timestamp; });
26
27 int FilesDeleted = 0;
28 for(auto FileEntry : m_vFileEntries)
29 {
30 if((int)m_vFileEntries.size() - FilesDeleted <= MaxEntries)
31 break;
32
33 char aBuf[IO_MAX_PATH_LENGTH];
34 if(m_aFileDesc[0] == '\0')
35 {
36 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s/%s", m_aPath, FileEntry.m_aFilename);
37 }
38 else
39 {
40 char aTimestring[TIMESTAMP_LENGTH];
41 str_timestamp_ex(time: FileEntry.m_Timestamp, buffer: aTimestring, buffer_size: sizeof(aTimestring), format: TimestampFormat::NOSPACE);
42 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s/%s_%s%s", m_aPath, m_aFileDesc, aTimestring, m_aFileExt);
43 }
44
45 m_pStorage->RemoveFile(pFilename: aBuf, Type: IStorage::TYPE_SAVE);
46 FilesDeleted++;
47 }
48}
49
50bool CFileCollection::ExtractTimestamp(const char *pTimestring, time_t *pTimestamp)
51{
52 // Discard anything after timestamp length from pTimestring (most likely the extension)
53 char aStrippedTimestring[TIMESTAMP_LENGTH];
54 str_copy(dst&: aStrippedTimestring, src: pTimestring);
55 return timestamp_from_str(string: aStrippedTimestring, format: TimestampFormat::NOSPACE, timestamp: pTimestamp);
56}
57
58bool CFileCollection::ParseFilename(const char *pFilename, time_t *pTimestamp)
59{
60 // Check if filename is valid
61 if(!str_endswith(str: pFilename, suffix: m_aFileExt))
62 return false;
63
64 const char *pTimestring = pFilename;
65
66 if(m_aFileDesc[0] == '\0')
67 {
68 int FilenameLength = str_length(str: pFilename);
69 if(m_FileExtLength + TIMESTAMP_LENGTH > FilenameLength)
70 {
71 return false;
72 }
73
74 pTimestring += FilenameLength - m_FileExtLength - TIMESTAMP_LENGTH + 1;
75 }
76 else
77 {
78 if(str_length(str: pFilename) != m_FileDescLength + TIMESTAMP_LENGTH + m_FileExtLength ||
79 !str_startswith(str: pFilename, prefix: m_aFileDesc))
80 return false;
81
82 pTimestring += m_FileDescLength + 1;
83 }
84
85 // Extract timestamp
86 if(!ExtractTimestamp(pTimestring, pTimestamp))
87 return false;
88
89 return true;
90}
91
92int CFileCollection::FilelistCallback(const char *pFilename, int IsDir, int StorageType, void *pUser)
93{
94 CFileCollection *pThis = static_cast<CFileCollection *>(pUser);
95
96 // Try to parse filename and extract timestamp
97 time_t Timestamp;
98 if(IsDir || !pThis->ParseFilename(pFilename, pTimestamp: &Timestamp))
99 return 0;
100
101 // Add the entry
102 pThis->m_vFileEntries.emplace_back(args&: Timestamp, args&: pFilename);
103
104 return 0;
105}
106